Skip to content

Commit

Permalink
feat: support Plates with other coordinate systems then 96 wells (#281)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: require explicit coordinate system value and type instead of implicit 96 well
  • Loading branch information
spawnia authored Sep 9, 2024
1 parent dc7e6be commit b6eaf5a
Show file tree
Hide file tree
Showing 13 changed files with 327 additions and 164 deletions.
3 changes: 3 additions & 0 deletions src/ImageMap/index.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Story } from '@storybook/react';
import React from 'react';

import { Plate } from '../Plate';
import { COORDINATE_SYSTEM_96_WELL } from '../Plate/coordinateSystem96Well';
import { TecanLayout } from '../index';

import { HiddenArea } from './HiddenArea';
Expand All @@ -22,6 +23,7 @@ export const TecanLayoutExample: Story = function Default() {
content: (
<Plate
data={[{ coordinates: { row: 'A', column: 3 }, content: 'test' }]}
coordinateSystem={COORDINATE_SYSTEM_96_WELL}
/>
),
},
Expand Down Expand Up @@ -54,6 +56,7 @@ export const Default: Story<ImageMapProps> = function Default(args) {
content={
<Plate
data={[{ coordinates: { row: 'A', column: 3 }, content: 'test' }]}
coordinateSystem={COORDINATE_SYSTEM_96_WELL}
/>
}
/>
Expand Down
26 changes: 19 additions & 7 deletions src/Plate/EmptyWell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,31 @@ import React from 'react';
import { PALETTE } from '../theme';

import { PLATE_FLOW } from './constants';
import { CoordinateSystem } from './types';
import { columnForPosition, rowForPosition } from './utils';
import { GENERAL_WELL_STYLE } from './wellUtils';

export function EmptyWell(props: { position: number }) {
export function EmptyWell<TCoordinateSystem extends CoordinateSystem>(props: {
position: number;
coordinateSystem: TCoordinateSystem;
}) {
const row = rowForPosition(
props.position,
PLATE_FLOW,
props.coordinateSystem,
);
const column = columnForPosition(
props.position,
PLATE_FLOW,
props.coordinateSystem,
);

const { setNodeRef, isOver } = useDroppable({
id: props.position,
data: {
coordinates: {
row: rowForPosition(props.position, PLATE_FLOW),
column: columnForPosition(props.position, PLATE_FLOW),
row,
column,
},
},
});
Expand All @@ -30,10 +45,7 @@ export function EmptyWell(props: { position: number }) {
alignItems: 'center',
}}
>
<small>
{rowForPosition(props.position, PLATE_FLOW) +
columnForPosition(props.position, PLATE_FLOW)}
</small>
<small>{row + column}</small>
</div>
);
}
15 changes: 10 additions & 5 deletions src/Plate/FilledWell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,24 @@ import React from 'react';
import { PALETTE } from '../theme';

import { PLATE_FLOW } from './constants';
import { PlateWell } from './types';
import { CoordinateSystem, PlateWell } from './types';
import { columnForPosition, rowForPosition } from './utils';
import { GENERAL_WELL_STYLE } from './wellUtils';

export function FilledWell(props: {
well: PlateWell;
export function FilledWell<TCoordinateSystem extends CoordinateSystem>(props: {
well: PlateWell<TCoordinateSystem>;
coordinateSystem: TCoordinateSystem;
position: number;
isDraggable: boolean;
}) {
const data = {
coordinates: {
row: rowForPosition(props.position, PLATE_FLOW),
column: columnForPosition(props.position, PLATE_FLOW),
row: rowForPosition(props.position, PLATE_FLOW, props.coordinateSystem),
column: columnForPosition(
props.position,
PLATE_FLOW,
props.coordinateSystem,
),
},
};

Expand Down
10 changes: 8 additions & 2 deletions src/Plate/RowLabel.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import React from 'react';

import { PLATE_FLOW } from './constants';
import { CoordinateSystem } from './types';
import { rowForPosition } from './utils';

export function RowLabel(props: { position: number }) {
export function RowLabel(props: {
position: number;
coordinateSystem: CoordinateSystem;
}) {
return (
<span
style={{
Expand All @@ -12,7 +16,9 @@ export function RowLabel(props: { position: number }) {
alignItems: 'center',
}}
>
<strong>{rowForPosition(props.position, PLATE_FLOW)}</strong>
<strong>
{rowForPosition(props.position, PLATE_FLOW, props.coordinateSystem)}
</strong>
</span>
);
}
14 changes: 10 additions & 4 deletions src/Plate/Well.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
import { Maybe } from '@mll-lab/js-utils';
import React from 'react';

import { EmptyWell } from './EmptyWell';
import { FilledWell } from './FilledWell';
import { PlateWell } from './types';
import { CoordinateSystem, PlateWell } from './types';

export function Well(props: {
export function Well<TCoordinateSystem extends CoordinateSystem>(props: {
position: number;
well?: PlateWell;
well: Maybe<PlateWell<TCoordinateSystem>>;
coordinateSystem: TCoordinateSystem;
isDraggable: boolean;
}) {
return props.well?.content ? (
<FilledWell
well={props.well}
coordinateSystem={props.coordinateSystem}
position={props.position}
isDraggable={props.isDraggable}
/>
) : (
<EmptyWell position={props.position} />
<EmptyWell
position={props.position}
coordinateSystem={props.coordinateSystem}
/>
);
}
20 changes: 2 additions & 18 deletions src/Plate/constants.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,4 @@
import { range } from 'lodash';
import { FlowDirection } from './types';

import { Coordinates, FlowDirection } from './types';

const TUBE_COUNT = 96;
export const WELLS = range(1, TUBE_COUNT + 1);
export const COORDINATES_COLUMNS = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
] as const satisfies ReadonlyArray<Coordinates['column']>;
export const COORDINATES_ROWS = [
'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
] as const satisfies ReadonlyArray<Coordinates['row']>;
/** Used internally when rendering the Plate component. */
export const PLATE_FLOW = 'row' as const satisfies FlowDirection;
8 changes: 8 additions & 0 deletions src/Plate/coordinateSystem96Well.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { CoordinateSystem } from './types';

export const COORDINATE_SYSTEM_96_WELL = {
rows: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'],
columns: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
} as const satisfies CoordinateSystem;

export type CoordinateSystem96Well = typeof COORDINATE_SYSTEM_96_WELL;
79 changes: 53 additions & 26 deletions src/Plate/index.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import React from 'react';

import { PALETTE } from '../theme';

import { COORDINATES_COLUMNS, COORDINATES_ROWS, WELLS } from './constants';
import {
COORDINATE_SYSTEM_96_WELL,
CoordinateSystem96Well,
} from './coordinateSystem96Well';
import { PlateProps, PlateWell } from './types';
import { coordinatesForPosition } from './utils';
import { allCoordinateSystemPositions, coordinatesForPosition } from './utils';

import { Plate } from './index';

Expand All @@ -18,22 +21,34 @@ export default {
},
};

const data: Array<PlateWell> = [
const data: Array<PlateWell<CoordinateSystem96Well>> = [
{
coordinates: { row: COORDINATES_ROWS[0], column: COORDINATES_COLUMNS[6] },
coordinates: {
row: COORDINATE_SYSTEM_96_WELL.rows[0],
column: COORDINATE_SYSTEM_96_WELL.columns[6],
},
content: <i>It renders any ReactNode</i>,
},
{
coordinates: { row: COORDINATES_ROWS[0], column: COORDINATES_COLUMNS[7] },
coordinates: {
row: COORDINATE_SYSTEM_96_WELL.rows[0],
column: COORDINATE_SYSTEM_96_WELL.columns[7],
},
content: 'Test',
color: PALETTE.red,
},
{
coordinates: { row: COORDINATES_ROWS[1], column: COORDINATES_COLUMNS[2] },
coordinates: {
row: COORDINATE_SYSTEM_96_WELL.rows[1],
column: COORDINATE_SYSTEM_96_WELL.columns[2],
},
content: 'Some text',
},
{
coordinates: { row: COORDINATES_ROWS[2], column: COORDINATES_COLUMNS[2] },
coordinates: {
row: COORDINATE_SYSTEM_96_WELL.rows[2],
column: COORDINATE_SYSTEM_96_WELL.columns[2],
},
content: (
<>
<p>Kontrolle</p>
Expand All @@ -45,27 +60,39 @@ const data: Array<PlateWell> = [
},
];

const rowFlowData: Array<PlateWell> = WELLS.map((well) => ({
coordinates: coordinatesForPosition(well, 'row'),
content: well,
}));
const COORDINATE_SYSTEM_96_WELL_POSITIONS = allCoordinateSystemPositions(
COORDINATE_SYSTEM_96_WELL,
);

const columnFlowData: Array<PlateWell> = WELLS.map((well) => ({
coordinates: coordinatesForPosition(well, 'column'),
content: well,
}));
const rowFlowData: Array<PlateWell<CoordinateSystem96Well>> =
COORDINATE_SYSTEM_96_WELL_POSITIONS.map((well) => ({
coordinates: coordinatesForPosition(well, 'row', COORDINATE_SYSTEM_96_WELL),
content: well,
}));

const Template: Story<Partial<PlateProps>> = function Template(args) {
return (
<Plate
data={null}
dndContextProps={{
onDragEnd: action('onDragEnd'), // dataLocation: `const sourceData = e.active.data.current; const targetData = e.over?.data.current;`
}}
{...args}
/>
);
};
const columnFlowData: Array<PlateWell<CoordinateSystem96Well>> =
COORDINATE_SYSTEM_96_WELL_POSITIONS.map((well) => ({
coordinates: coordinatesForPosition(
well,
'column',
COORDINATE_SYSTEM_96_WELL,
),
content: well,
}));

const Template: Story<Partial<PlateProps<CoordinateSystem96Well>>> =
function Template(args) {
return (
<Plate
data={null}
coordinateSystem={COORDINATE_SYSTEM_96_WELL}
dndContextProps={{
onDragEnd: action('onDragEnd'), // dataLocation: `const sourceData = e.active.data.current; const targetData = e.over?.data.current;`
}}
{...args}
/>
);
};

export const Default = Template.bind({});
Default.args = {
Expand Down
4 changes: 3 additions & 1 deletion src/Plate/index.test.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { render } from '@testing-library/react';
import React from 'react';

import { COORDINATE_SYSTEM_96_WELL } from './coordinateSystem96Well';

import { Plate } from './index';

describe('Plate', () => {
it('renders without data', () => {
render(<Plate data={null} />);
render(<Plate data={null} coordinateSystem={COORDINATE_SYSTEM_96_WELL} />);
});
});
Loading

0 comments on commit b6eaf5a

Please sign in to comment.