Skip to content

Commit

Permalink
feat(components): set size of display components or resize to fit con…
Browse files Browse the repository at this point in the history
…tainer #191

BREAKING_CHANGE: all display components have a default width 100% and height
  • Loading branch information
JonasKellerer committed May 13, 2024
1 parent 418be98 commit 5019cfe
Show file tree
Hide file tree
Showing 55 changed files with 367 additions and 152 deletions.
10 changes: 5 additions & 5 deletions components/src/preact/aggregatedData/aggregate.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const meta: Meta<AggregateProps> = {
component: Aggregate,
argTypes: {
fields: [{ control: 'object' }],
size: [{ control: 'object' }],
},
parameters: {
fetchMock: {
Expand Down Expand Up @@ -37,17 +38,16 @@ export default meta;

export const Default: StoryObj<AggregateProps> = {
render: (args) => (
<div class='max-w-screen-lg'>
<LapisUrlContext.Provider value={LAPIS_URL}>
<Aggregate {...args} />
</LapisUrlContext.Provider>
</div>
<LapisUrlContext.Provider value={LAPIS_URL}>
<Aggregate {...args} />
</LapisUrlContext.Provider>
),
args: {
fields: ['division', 'host'],
views: ['table'],
filter: {
country: 'USA',
},
size: { width: '100%', height: '70vh' },
},
};
12 changes: 8 additions & 4 deletions components/src/preact/aggregatedData/aggregate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Headline from '../components/headline';
import Info from '../components/info';
import { LoadingDisplay } from '../components/loading-display';
import { NoDataDisplay } from '../components/no-data-display';
import { ResizeContainer, type Size } from '../components/resize-container';
import Tabs from '../components/tabs';
import { useQuery } from '../useQuery';

Expand All @@ -20,9 +21,10 @@ export interface AggregateProps {
filter: LapisFilter;
fields: string[];
views: View[];
size?: Size;
}

export const Aggregate: FunctionComponent<AggregateProps> = ({ fields, views, filter }) => {
export const Aggregate: FunctionComponent<AggregateProps> = ({ fields, views, filter, size }) => {
const lapis = useContext(LapisUrlContext);

const { data, error, isLoading } = useQuery(async () => {
Expand Down Expand Up @@ -56,9 +58,11 @@ export const Aggregate: FunctionComponent<AggregateProps> = ({ fields, views, fi
}

return (
<Headline heading={headline}>
<AggregatedDataTabs data={data} views={views} fields={fields} />
</Headline>
<ResizeContainer size={size} defaultSize={{ height: '700px', width: '100%' }}>
<Headline heading={headline}>
<AggregatedDataTabs data={data} views={views} fields={fields} />
</Headline>
</ResizeContainer>
);
};

Expand Down
20 changes: 16 additions & 4 deletions components/src/preact/components/headline.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
import { type FunctionComponent } from 'preact';
import { useEffect, useRef, useState } from 'preact/hooks';

export interface HeadlineProps {
heading: string;
}

const Headline: FunctionComponent<HeadlineProps> = ({ heading, children }) => {
const ref = useRef<HTMLHeadingElement>(null);

const [h1Height, setH1Height] = useState('2rem');

useEffect(() => {
if (ref.current) {
const h1Height = ref.current.getBoundingClientRect().height;
setH1Height(`${h1Height}px`);
}
}, []);

return (
<>
<h1>{heading}</h1>
{children}
</>
<div className='h-full w-full'>
<h1 ref={ref}>{heading}</h1>
<div style={{ height: `calc(100% - ${h1Height})` }}>{children}</div>
</div>
);
};

Expand Down
23 changes: 23 additions & 0 deletions components/src/preact/components/resize-container.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { type FunctionComponent } from 'preact';

export type Size = {
width?: string;
height?: string;
};

export interface ResizeContainerProps {
size?: Size;
defaultSize: Size;
}

export const ResizeContainer: FunctionComponent<ResizeContainerProps> = ({ children, size, defaultSize }) => {
return <div style={extendByDefault(size, defaultSize)}>{children}</div>;
};

const extendByDefault = (size: Size | undefined, defaultSize: Size) => {
if (size === undefined) {
return defaultSize;
}

return { ...defaultSize, ...size };
};
1 change: 1 addition & 0 deletions components/src/preact/components/table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { type OneDArray, type TColumn, type TData } from 'gridjs/dist/src/types'
import { type PaginationConfig } from 'gridjs/dist/src/view/plugin/pagination';
import { type ComponentChild } from 'preact';
import { useEffect, useRef } from 'preact/hooks';

import 'gridjs/dist/theme/mermaid.css';

export const tableStyle = {
Expand Down
4 changes: 2 additions & 2 deletions components/src/preact/components/tabs.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ export const TabsWithToolbarOnlyShowingOnSecondTab: StoryObj = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);

await waitFor(() => expect(canvas.getByLabelText('FirstTab', { selector: 'input' })).toBeVisible());
await waitFor(() => expect(canvas.getByRole('button', { name: 'SecondTab' })).toBeVisible());
await expect(canvas.queryByText('Toolbar')).not.toBeInTheDocument();

await fireEvent.click(canvas.getByLabelText('SecondTab', { selector: 'input' }));
await fireEvent.click(canvas.getByRole('button', { name: 'SecondTab' }));
await waitFor(() => expect(canvas.getByText('Toolbar')).toBeVisible());
},
};
71 changes: 47 additions & 24 deletions components/src/preact/components/tabs.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Fragment, type FunctionComponent } from 'preact';
import { useState } from 'preact/hooks';
import { useEffect, useRef, useState } from 'preact/hooks';
import { type JSXInternal } from 'preact/src/jsx';

type Tab = {
Expand All @@ -14,34 +14,57 @@ interface ComponentTabsProps {

const Tabs: FunctionComponent<ComponentTabsProps> = ({ tabs, toolbar }) => {
const [activeTab, setActiveTab] = useState(tabs[0].title);
const [heightOfTabs, setHeightOfTabs] = useState('3rem');
const tabRef = useRef<HTMLDivElement>(null);

const tabNames = tabs.map((tab) => tab.title).join(', ');

const tabElements = tabs.map((tab) => {
return (
<Fragment key={tab.title}>
<input
type='radio'
name={tabNames}
role='tab'
className='tab'
aria-label={tab.title}
checked={activeTab === tab.title}
onChange={() => setActiveTab(tab.title)}
/>
<div role='tabpanel' className='tab-content bg-base-100 border-base-300 rounded-box p-1'>
{tab.content}
</div>
</Fragment>
);
});
useEffect(() => {
if (tabRef.current) {
const heightOfTabs = tabRef.current.getBoundingClientRect().height;
setHeightOfTabs(`${heightOfTabs}px`);
}
}, []);

const tabElements = (
<div className='flex flex-row'>
{tabs.map((tab) => {
return (
<Fragment key={tab.title}>
<button
className={`px-4 py-2 text-sm font-medium leading-5 transition-colors duration-150 ${
activeTab === tab.title
? 'border-b-2 border-gray-400'
: 'text-gray-600 hover:bg-gray-100 hover:text-gray-700'
}`}
onClick={() => {
setActiveTab(tab.title);
}}
>
{tab.title}
</button>
</Fragment>
);
})}
</div>
);

const toolbarElement = typeof toolbar === 'function' ? toolbar(activeTab) : toolbar;

return (
<div role='tablist' className='tabs tabs-lifted'>
{tabElements}
{toolbar && <div className='m-1 col-[9999]'>{toolbarElement}</div>}
<div className='h-full w-full'>
<div ref={tabRef} className='flex flex-row justify-between'>
{tabElements}
{toolbar && <div className='py-2'>{toolbarElement}</div>}
</div>
<div
className={`p-2 border-2 border-gray-100 rounded-b-md rounded-tr-md ${activeTab === tabs[0].title ? '' : 'rounded-tl-md'}`}
style={{ height: `calc(100% - ${heightOfTabs})` }}
>
{tabs.map((tab) => (
<div className='h-full overflow-auto' key={tab.title} hidden={activeTab !== tab.title}>
{tab.content}
</div>
))}
</div>
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export const MutationComparisonVenn: FunctionComponent<MutationComparisonVennPro
type: 'venn',
data: sets,
options: {
maintainAspectRatio: false,
scales: {
x: {
ticks: {
Expand Down Expand Up @@ -114,9 +115,11 @@ export const MutationComparisonVenn: FunctionComponent<MutationComparisonVennPro
}

return (
<>
<GsChart configuration={config} />
<div className='h-full flex flex-col'>
<div className='flex-1'>
<GsChart configuration={config} />
</div>
<div class='flex flex-wrap break-words m-2' ref={divRef} />
</>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const meta: Meta<MutationComparisonProps> = {
options: ['table', 'venn'],
control: { type: 'check' },
},
size: [{ control: 'object' }],
},
parameters: {
fetchMock: {
Expand Down Expand Up @@ -75,7 +76,12 @@ const Template: StoryObj<MutationComparisonProps> = {
render: (args) => (
<LapisUrlContext.Provider value={LAPIS_URL}>
<ReferenceGenomeContext.Provider value={referenceGenome}>
<MutationComparison variants={args.variants} sequenceType={args.sequenceType} views={args.views} />
<MutationComparison
variants={args.variants}
sequenceType={args.sequenceType}
views={args.views}
size={args.size}
/>
</ReferenceGenomeContext.Provider>
</LapisUrlContext.Provider>
),
Expand All @@ -101,6 +107,7 @@ export const TwoVariants: StoryObj<MutationComparisonProps> = {
],
sequenceType: 'nucleotide',
views: ['table', 'venn'],
size: { width: '100%', height: '700px' },
},
};

Expand Down
17 changes: 13 additions & 4 deletions components/src/preact/mutationComparison/mutation-comparison.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { type DisplayedMutationType, MutationTypeSelector } from '../components/
import { NoDataDisplay } from '../components/no-data-display';
import { type ProportionInterval } from '../components/proportion-selector';
import { ProportionSelectorDropdown } from '../components/proportion-selector-dropdown';
import { ResizeContainer, type Size } from '../components/resize-container';
import Tabs from '../components/tabs';
import { useQuery } from '../useQuery';

Expand All @@ -31,9 +32,15 @@ export interface MutationComparisonProps {
variants: MutationComparisonVariant[];
sequenceType: SequenceType;
views: View[];
size?: Size;
}

export const MutationComparison: FunctionComponent<MutationComparisonProps> = ({ variants, sequenceType, views }) => {
export const MutationComparison: FunctionComponent<MutationComparisonProps> = ({
variants,
sequenceType,
views,
size,
}) => {
const lapis = useContext(LapisUrlContext);

const { data, error, isLoading } = useQuery(async () => {
Expand Down Expand Up @@ -67,9 +74,11 @@ export const MutationComparison: FunctionComponent<MutationComparisonProps> = ({
}

return (
<Headline heading={headline}>
<MutationComparisonTabs data={data.mutationData} sequenceType={sequenceType} views={views} />
</Headline>
<ResizeContainer size={size} defaultSize={{ height: '700px', width: '100%' }}>
<Headline heading={headline}>
<MutationComparisonTabs data={data.mutationData} sequenceType={sequenceType} views={views} />
</Headline>
</ResizeContainer>
);
};

Expand Down
9 changes: 8 additions & 1 deletion components/src/preact/mutations/mutations.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const meta: Meta<MutationsProps> = {
options: ['table', 'grid', 'insertions'],
control: { type: 'check' },
},
size: [{ control: 'object' }],
},
};

Expand All @@ -31,7 +32,12 @@ const Template = {
render: (args: MutationsProps) => (
<LapisUrlContext.Provider value={LAPIS_URL}>
<ReferenceGenomeContext.Provider value={referenceGenome}>
<Mutations variant={args.variant} sequenceType={args.sequenceType} views={args.views} />
<Mutations
variant={args.variant}
sequenceType={args.sequenceType}
views={args.views}
size={args.size}
/>
</ReferenceGenomeContext.Provider>
</LapisUrlContext.Provider>
),
Expand All @@ -43,6 +49,7 @@ export const Default: StoryObj<MutationsProps> = {
variant: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateTo: '2022-01-01' },
sequenceType: 'nucleotide',
views: ['grid', 'table', 'insertions'],
size: { width: '100%', height: '700px' },
},
parameters: {
fetchMock: {
Expand Down
12 changes: 8 additions & 4 deletions components/src/preact/mutations/mutations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { type DisplayedMutationType, MutationTypeSelector } from '../components/
import { NoDataDisplay } from '../components/no-data-display';
import type { ProportionInterval } from '../components/proportion-selector';
import { ProportionSelectorDropdown } from '../components/proportion-selector-dropdown';
import { ResizeContainer, type Size } from '../components/resize-container';
import Tabs from '../components/tabs';
import { useQuery } from '../useQuery';

Expand All @@ -33,9 +34,10 @@ export interface MutationsProps {
variant: LapisFilter;
sequenceType: SequenceType;
views: View[];
size?: Size;
}

export const Mutations: FunctionComponent<MutationsProps> = ({ variant, sequenceType, views }) => {
export const Mutations: FunctionComponent<MutationsProps> = ({ variant, sequenceType, views, size }) => {
const lapis = useContext(LapisUrlContext);
const { data, error, isLoading } = useQuery(async () => {
return queryMutationsData(variant, sequenceType, lapis);
Expand Down Expand Up @@ -67,9 +69,11 @@ export const Mutations: FunctionComponent<MutationsProps> = ({ variant, sequence
}

return (
<Headline heading={headline}>
<MutationsTabs mutationsData={data} sequenceType={sequenceType} views={views} />
</Headline>
<ResizeContainer size={size} defaultSize={{ height: '700px', width: '100%' }}>
<Headline heading={headline}>
<MutationsTabs mutationsData={data} sequenceType={sequenceType} views={views} />
</Headline>
</ResizeContainer>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const PrevalenceOverTimeBarChart = ({
datasets: data.map((graphData, index) => datasets(graphData, index, confidenceIntervalMethod)),
},
options: {
maintainAspectRatio: false,
animation: false,
scales: {
y: getYAxisScale(yAxisScaleType),
Expand Down

0 comments on commit 5019cfe

Please sign in to comment.