Skip to content

feat(MasterMix): distinguish per reaction ingredients and add recipe mode - #330

Merged
simbig merged 33 commits into
masterfrom
feat-master-mix-per-reaction-ingredients
Aug 3, 2026
Merged

feat(MasterMix): distinguish per reaction ingredients and add recipe mode#330
simbig merged 33 commits into
masterfrom
feat-master-mix-per-reaction-ingredients

Conversation

@simbig

@simbig simbig commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Problem

MasterMix scaled every ingredient by count plus pipetting loss, which is wrong for anything pipetted per well — template, standard, filler water. In limes-frontend (AE-3800) that asked for 110 µl cDNA in the shared mix and reported 440 µl instead of 330 µl. count and pipettingLoss were also required, though a recipe has no run yet.

Approach

  • perReactionIngredients — outside the master mix and its sum, no pipetting loss, followed by the resulting Reaktionsvolumen.
  • mode="recipe" — a union with the scaled mode, so neither can be half-specified.
  • One table, every row indented one step below the total it is a summand of, so the master mix meets the per reaction ingredients on the level of the reaction volume. Its sum carries the name, being both the amount to mix up front and the amount per reaction.
  • Pure reactionVolume(mix) export. A callback cannot work: the molarity goes into title, which is input to MasterMix.

Table

The pointer cursor came from probing onRow({}) with a fabricated record behind a @ts-expect-error and then covered every row. It is now derived per row from that row's own onClick, since only ingredients can be checked off here, not the totals.

Compatibility

Existing limes-frontend and nemo callers pass name, count, ingredients and pipettingLoss, which still matches. MasterMixProps is narrower — count and pipettingLoss live on the scaled arm of the union only — but no consumer imports that type, so this ships as a minor.

🤖 Generated with Claude Code

simbig added 3 commits July 30, 2026 15:05
… mix

Ingredients like template or standard are added to each reaction
individually. They belong to the reaction volume, but must never be
multiplied by the number of Ansätze or included in the master mix sum.

Callers pass them via the new optional `perReactionIngredients` prop.
They render below the Gesamtvolumen row, without a pipetting loss column
value, followed by a Reaktionsvolumen row. The exported `reactionVolume`
saves callers from summing both lists themselves, e.g. to calculate
molarities.

Pagination is disabled because a summary row paginated onto the second
page is misleading. No caller currently exceeds the previous page size of
10 rows, so nothing changes visually today.

🤖 Generated with Claude Code
The flat table did not convey that the master mix is part of the reaction
mix, and the title read "<name> MasterMix" even with per reaction
ingredients present.

- render the master mix in its own card inside the reaction mix card
- name the mix after what it actually is: Reaktionsmix or MasterMix
- move the per reaction rows into a table without onRow, so Table stops
  injecting the pointer cursor on rows that are not clickable
- split the component into one file per component

🤖 Generated with Claude Code
One table replaces the two that had to be aligned by hand, which removes the
need for fixed column widths and lets every volume be right aligned.

The master mix now appears as a labelled block inside the reaction mix,
closed off by its total, so what follows reads as added per reaction.
Ingredients are indented to reserve room for a check mark that marks them as
pipetted without shifting the layout.

`mode="recipe"` shows the mix unscaled, for contexts that have no run yet and
therefore no meaningful number of reactions.

Visible for existing callers: the card hugs the table instead of stretching
across the surrounding layout, ingredients carry the indent for the check
mark, and volumes for a single reaction are dimmed where a scaled column
shows the amount to pipette.

🤖 Generated with Claude Code
@simbig simbig changed the title feat(MasterMix): distinguish per reaction ingredients from the master mix feat(MasterMix): distinguish per reaction ingredients and add recipe mode Jul 30, 2026
simbig added 12 commits July 30, 2026 17:42
Both ingredient lists are keyed by the consumer and existing callers number
each list from 1, so passing the same key in both collided into one React row
key. React then warns about duplicate children and remounts the affected row
on every re-render, e.g. on each pipetted toggle.

🤖 Generated with Claude Code
The default branch would have multiplied any row kind added later by the
number of reactions plus pipetting loss, showing a wrong volume on a work
list without a compile error.

🤖 Generated with Claude Code
The left edge used dividerColor, which is gray4 and therefore invisible
against the gray3 background of the total row, so the box looked open on
its left side exactly where the closing line is darkest.

🤖 Generated with Claude Code
Only used by the component that renders it.

🤖 Generated with Claude Code
Hugging the content made the card width depend on the ingredient names, so
cards placed side by side in a grid no longer lined up. Capping the table
keeps it from running wide without the card claiming a width of its own.

🤖 Generated with Claude Code
Without per reaction ingredients there is no section row to indent against,
so the ingredients sat 20px right of their own total. Indenting every row
the master mix consists of also delimits the block well enough that the gap
before the per reaction rows became unnecessary.

The predicate for those rows already existed for scaling, so both the indent
and the scaling now derive from one definition of what the master mix is.

🤖 Generated with Claude Code
The separate label row said what the block was, while the row below it said
only Gesamtvolumen — yet that volume is what goes into each reaction, so it
is the line item the label belongs on. Naming it there drops a row, puts the
master mix on the same level as the other per reaction rows, and leaves every
row with a volume, which collapses the row union back into one shape.

🤖 Generated with Claude Code
Indenting the master mix total kept its own ingredients aligned with it, but
broke the column the pipettor actually adds up: master mix plus template
equals the reaction volume. Outdented totals also follow the convention any
invoice or recipe uses, and the indent no longer depends on whether the mix
is nested.

🤖 Generated with Claude Code
Indenting only the master mix ingredients could not be derived from either
reading of what an indent means here, so the master mix total and the
template ended up on different levels although both are added per reaction.
One step per sum puts them side by side below the reaction volume, and makes
the nesting itself carry what the box previously had to.

🤖 Generated with Claude Code
The left edge bracketed the master mix from outside the block it enclosed,
which the indentation already expresses.
A caller without types that passes no pipetting loss now falls back to the
recipe view instead of dereferencing it.
@simbig

simbig commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

Self-reviewed at 306bcb1

🤖 Posted by Claude Code

simbig added 13 commits July 30, 2026 19:38
…reaction

The predicate appeared verbatim at four sites in three files, each time in a
different disguise. Naming it also names what "nested" left implicit.
Both return branches spelled out the same prefix.
Both arms of the union are new and unreleased, and MasterMixProps is enough to
annotate a call site. Removing them after the release would be breaking.
The cursor was decided for the whole table by calling onRow with an empty
object, which lies to a callback that is typed for a record and only worked
because no caller dereferenced it. Deriving it from the row props each row
actually returns drops the workaround MasterMix needed to opt single rows out.
Both rules were only reachable through a rendered table, so nothing would turn
red if the nesting were flattened or the row keys collided again.
The closing line exists exactly when rows follow the total, which the selector
can say by itself. Deciding it in the row class instead required threading the
nesting through rowClassName, and made the two total rows differ where they do
not.
Table defaults to "id", so the field has to be named, but not through a closure.
@simbig
simbig marked this pull request as ready for review July 30, 2026 19:12
@simbig
simbig requested a review from mic-web July 30, 2026 19:12
Comment thread src/Table/index.tsx Outdated
Co-authored-by: mic-web <4804412+mic-web@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Updates MasterMix to correctly separate ingredients that are pipetted per well from the shared master mix, and introduces a “recipe” (unscaled) mode so mixes can be displayed without run-specific scaling inputs.

Changes:

  • Add perReactionIngredients support and compute a separate reaction total volume (master mix + per-reaction additions).
  • Introduce mode="recipe" via a discriminated union in MasterMixProps, and adjust rendering/interaction accordingly.
  • Refactor the UI into a dedicated MixTable with indentation, row styling, and pipetting-loss behavior limited to master-mix rows; update generic Table cursor behavior.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/Table/index.tsx Derives per-row pointer cursor from that row’s onClick instead of a global hover rule.
src/MasterMix/VolumeTable.tsx Adds shared styling/constants for total, pipetted, and reference-volume rows.
src/MasterMix/volumeColumns.tsx Introduces column rendering for names, per-reaction volumes, and optional scaled volumes with PV.
src/MasterMix/types.ts Adds ReactionMix, perReactionIngredients, recipe/scaled union props, and MasterMixTableRow.
src/MasterMix/reactionVolume.ts Adds reactionVolume (and sumVolume) that includes per-reaction ingredients.
src/MasterMix/reactionVolume.test.ts Adds unit tests for reactionVolume.
src/MasterMix/pipettingLossTableColumn.tsx Applies PV only to master-mix rows; renders “–” for per-reaction and reaction-total rows.
src/MasterMix/pipettingLossTableColumn.test.tsx Updates tests to the new row model and PV rendering rules.
src/MasterMix/MixTable.tsx New table component that assembles rows, applies classes, and handles “pipetted” checkoff clicks.
src/MasterMix/mixRows.ts Builds hierarchical rows (ingredients → totals → per-reaction additions → reaction total).
src/MasterMix/mixRows.test.ts Adds tests for row ordering, totals, and key namespacing.
src/MasterMix/MasterMixRowName.tsx New indented row-name renderer with an inline pipetted check mark.
src/MasterMix/MasterMix.test.tsx Updates/expands integration tests for reaction-mix rendering, PV exclusion, and recipe mode behavior.
src/MasterMix/index.tsx Refactors MasterMix to use MixTable, support reaction mixes, and export reactionVolume.
src/MasterMix/index.stories.tsx Adjusts stories for new props and adds per-reaction and recipe examples.
src/MasterMix/indentLevel.ts Computes indentation levels for the hierarchical table layout.
src/MasterMix/indentLevel.test.ts Adds tests for indentation behavior across row kinds.
src/MasterMix/hasPerReactionIngredients.ts Adds a type-guard helper to detect per-reaction ingredients.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/MasterMix/index.tsx
@simbig
simbig merged commit 0817ab1 into master Aug 3, 2026
8 checks passed
@simbig
simbig deleted the feat-master-mix-per-reaction-ingredients branch August 3, 2026 06:56
github-actions Bot pushed a commit that referenced this pull request Aug 3, 2026
# [20.5.0](v20.4.1...v20.5.0) (2026-08-03)

### Features

* **MasterMix:** distinguish per reaction ingredients and add recipe mode ([#330](#330)) ([0817ab1](0817ab1))
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 20.5.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Development

Successfully merging this pull request may close these issues.

3 participants