Skip to content

feat(automation): add hydraulic valve shuffle components - #1059

Open
ludvigovrevik wants to merge 8 commits into
developfrom
feat/hydraulic-valve-symbols
Open

feat(automation): add hydraulic valve shuffle components#1059
ludvigovrevik wants to merge 8 commits into
developfrom
feat/hydraulic-valve-symbols

Conversation

@ludvigovrevik

@ludvigovrevik ludvigovrevik commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Adds the three hydraulic valve symbols from OpenBridge 6.1: the 4/3 directional valve, the X/2 valve (2/2, 3/2, 4/2) and the check valve.

Try them in the Storybook preview: Valve 4/3 · Valve X/2 · Check valve

@tibnor — could you review the code here? Three design choices I'd especially like your take on:

  1. One shared "shuffle" base class. All three Figma components use the same inner mechanism: the selected position symbol always sits in the fixed center slot, and the other options shuffle to either side of it, so the total width never changes. I built that once as an abstract ObcShuffleButtonBase, and the valves are thin subclasses that just supply their icons. Is that the right split, or would you prefer three independent components?
  2. Controlled selection. Clicking an option only fires position-selected — the symbol doesn't move until the app sets selectedPosition. Reasoning: the actual valve position is plant feedback, and the UI shouldn't show a position the device never reached. (The stories wire the event back to the property so it feels live in Storybook.) OK, or should it update itself?
  3. Animation — keep or remove? Selection changes slide over 100 ms (a single transform transition, disabled under prefers-reduced-motion). The Figma frames are all marked WIP and don't specify motion, so this is a guess — happy to remove it.

What's inside

  • obc-hydraulic-valve-4-3 — 3 positions, 5 type variants for the center symbol
  • obc-hydraulic-valve-x-2 — 2 positions, types 2/2, 3/2, 4/2
  • obc-hydraulic-check-valve — static single symbol, display-only
  • Keyboard support follows the WAI-ARIA radio-group pattern: one tab stop, arrow keys move the selection with wrap-around
  • All sizes come from the global size tokens, so the components scale with the obc-component-size-* classes; colors are the automation pipe/device tokens; every icon already existed in the library (obi-hydraulic-*) — no new SVG
  • Stories are tagged wip while the design is WIP in Figma

Figma: 4/3 valve · X/2 valve · check valve

Testing

  • Unit tests for the slot/offset layout math (6 passing)
  • 12 stories; Linux visual baselines generated with the project Docker image, re-run for stability and checked against the Figma renders
  • typecheck, lint and prettier all green

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added new hydraulic valve web components (Hydraulic 4/3, Hydraulic X/2, and a check-valve symbol) with accessible labeling.
    • Introduced an accessible shuffle-button selector with keyboard navigation and position-selected event support.
    • Added Storybook stories to preview valve configurations and selectable positions.
  • Style
    • Updated shuffle-button visuals, including reduced-motion behavior and selection/hover styling.
    • Adjusted check-valve thumbnail cursor styling.
  • Tests
    • Added unit tests for shuffle-button layout helpers (clamping, slot sizing, and window offsets).

Add three components built on a shared shuffle-button base where the
selected position thumb always occupies the fixed center slot and option
thumbs redistribute around it:

- obc-hydraulic-valve-4-3: 4/3 directional valve, 5 type variants
- obc-hydraulic-valve-x-2: 2/2, 3/2 and 4/2 two-position valves
- obc-hydraulic-check-valve: static check valve symbol

Controlled interaction (position-selected event), WAI-ARIA radio-group
keyboard navigation, sizes derived from global size tokens so components
scale with obc-component-size-* classes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a reusable accessible shuffle-button base with layout helpers, styling, keyboard and event handling. Adds configurable 2-position and 4/3 hydraulic valves, a static check valve, custom-element typings, Storybook stories, and layout tests.

Changes

Hydraulic controls

Layer / File(s) Summary
Shuffle selector foundation
packages/openbridge-webcomponents/src/automation/shuffle-button/*
Adds layout helpers, accessible controlled selection, keyboard navigation, focus management, selection events, styling, and unit tests.
2-position hydraulic valve
packages/openbridge-webcomponents/src/automation/hydraulic-valve-x-2/*
Adds configurable 2-position valve rendering and Storybook variants for valve types and positions.
4/3 hydraulic valve
packages/openbridge-webcomponents/src/automation/hydraulic-valve-4-3/*
Adds five center-position variants, three-position rendering, and Storybook controls and stories.
Static check valve component
packages/openbridge-webcomponents/src/automation/hydraulic-check-valve/*
Adds an accessible static check-valve component with styling and a default Storybook story.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant HydraulicValve
  participant ObcShuffleButtonBase
  participant Storybook
  User->>HydraulicValve: click thumb or press Arrow key
  HydraulicValve->>ObcShuffleButtonBase: handle selection request
  ObcShuffleButtonBase->>Storybook: dispatch position-selected
  Storybook->>HydraulicValve: update selectedPosition
  HydraulicValve->>ObcShuffleButtonBase: render selected position
Loading

Suggested reviewers: tibnor

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: new hydraulic valve shuffle components in automation.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/hydraulic-valve-symbols

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
packages/openbridge-webcomponents/src/automation/hydraulic-check-valve/hydraulic-check-valve.ts (1)

8-21: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Complete the required component documentation sections.

The class JSDoc has a summary and usage guidance, but it omits the required features/variants and best-practice/constraint guidance, including when this display-only component should not be used. Add those sections; there are no slots or events to document here.

As per coding guidelines, component JSDoc must include overview, features/variants, usage guidance, slots/content, events, and best practices.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/openbridge-webcomponents/src/automation/hydraulic-check-valve/hydraulic-check-valve.ts`
around lines 8 - 21, Expand the class JSDoc for the hydraulic check-valve
component with the required features/variants and best-practices/constraints
sections, explicitly stating when the display-only component should not be used.
Retain the existing overview and usage guidance, and document that there are no
slots/content or events.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/openbridge-webcomponents/src/automation/hydraulic-valve-x-2/hydraulic-valve-x-2.ts`:
- Around line 23-49: Update the component JSDoc in
packages/openbridge-webcomponents/src/automation/hydraulic-valve-x-2/hydraulic-valve-x-2.ts
lines 23-49 and
packages/openbridge-webcomponents/src/automation/hydraulic-valve-4-3/hydraulic-valve-4-3.ts
lines 26-52: add practical controlled-selection best-practice guidance, move
`@alpha` before the final tag block, and ensure that final block contains only the
existing `@fires/`@event and `@slot` tags.

In
`@packages/openbridge-webcomponents/src/automation/shuffle-button/shuffle-button-base.ts`:
- Around line 46-49: Update the ariaLabel property in the shuffle-button base
component to use a meaningful non-empty default accessible name, ensuring the
rendered radiogroup is labeled when consumers do not provide a value. Preserve
consumer overrides and apply the default at the base property rather than
requiring changes to each concrete valve component.
- Around line 34-35: Remove the `@fires` position-selected declaration from the
abstract shuffle-button base class and add it to each concrete `@customElement`
class that extends it. Keep the existing event name and description unchanged,
placing the manifest tag in each concrete component’s class documentation.

In
`@packages/openbridge-webcomponents/src/automation/shuffle-button/shuffle-layout.ts`:
- Around line 7-9: Update clampPosition to normalize a NaN position to the
fallback position before rounding and clamping, ensuring it returns a valid
index within the available range; add a regression test covering NaN input and
the resulting valid position.

---

Nitpick comments:
In
`@packages/openbridge-webcomponents/src/automation/hydraulic-check-valve/hydraulic-check-valve.ts`:
- Around line 8-21: Expand the class JSDoc for the hydraulic check-valve
component with the required features/variants and best-practices/constraints
sections, explicitly stating when the display-only component should not be used.
Retain the existing overview and usage guidance, and document that there are no
slots/content or events.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3adfd624-8954-47e2-9fa7-e2aeec6d0dc4

📥 Commits

Reviewing files that changed from the base of the PR and between 5c94459 and ea19c32.

📒 Files selected for processing (11)
  • packages/openbridge-webcomponents/src/automation/hydraulic-check-valve/hydraulic-check-valve.css
  • packages/openbridge-webcomponents/src/automation/hydraulic-check-valve/hydraulic-check-valve.stories.ts
  • packages/openbridge-webcomponents/src/automation/hydraulic-check-valve/hydraulic-check-valve.ts
  • packages/openbridge-webcomponents/src/automation/hydraulic-valve-4-3/hydraulic-valve-4-3.stories.ts
  • packages/openbridge-webcomponents/src/automation/hydraulic-valve-4-3/hydraulic-valve-4-3.ts
  • packages/openbridge-webcomponents/src/automation/hydraulic-valve-x-2/hydraulic-valve-x-2.stories.ts
  • packages/openbridge-webcomponents/src/automation/hydraulic-valve-x-2/hydraulic-valve-x-2.ts
  • packages/openbridge-webcomponents/src/automation/shuffle-button/shuffle-button-base.ts
  • packages/openbridge-webcomponents/src/automation/shuffle-button/shuffle-button.css
  • packages/openbridge-webcomponents/src/automation/shuffle-button/shuffle-layout.spec.ts
  • packages/openbridge-webcomponents/src/automation/shuffle-button/shuffle-layout.ts

Comment on lines +34 to +35
* @fires position-selected {PositionSelectedEvent} Position requested by the user.
* @ignore

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Move the event manifest tag to concrete components.

@fires on this abstract base violates the component-documentation contract; document position-selected on each concrete @customElement class instead.

As per coding guidelines, “@slot and @fires tags must live on the concrete @customElement class.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/openbridge-webcomponents/src/automation/shuffle-button/shuffle-button-base.ts`
around lines 34 - 35, Remove the `@fires` position-selected declaration from the
abstract shuffle-button base class and add it to each concrete `@customElement`
class that extends it. Keep the existing event name and description unchanged,
placing the manifest tag in each concrete component’s class documentation.

Source: Coding guidelines

Comment on lines +7 to +9
export function clampPosition(positionCount: number, position: number): number {
return Math.min(Math.max(Math.round(position), 0), positionCount - 1);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Normalize NaN before clamping.

A public selectedPosition = NaN produces NaN here, leaving every radio unselected/non-tabbable and emitting an invalid CSS offset. Treat NaN as a fallback position and add a regression test.

Proposed fix
 export function clampPosition(positionCount: number, position: number): number {
-  return Math.min(Math.max(Math.round(position), 0), positionCount - 1);
+  const roundedPosition = Number.isNaN(position) ? 0 : Math.round(position);
+  return Math.min(Math.max(roundedPosition, 0), positionCount - 1);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function clampPosition(positionCount: number, position: number): number {
return Math.min(Math.max(Math.round(position), 0), positionCount - 1);
}
export function clampPosition(positionCount: number, position: number): number {
const roundedPosition = Number.isNaN(position) ? 0 : Math.round(position);
return Math.min(Math.max(roundedPosition, 0), positionCount - 1);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/openbridge-webcomponents/src/automation/shuffle-button/shuffle-layout.ts`
around lines 7 - 9, Update clampPosition to normalize a NaN position to the
fallback position before rounding and clamping, ensuring it returns a valid
index within the available range; add a regression test covering NaN input and
the resulting valid position.

ludvigovrevik and others added 4 commits July 24, 2026 00:41
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Normalize NaN selectedPosition to position 0 (+ regression test)
- Non-empty default ariaLabel on the base class
- Move @fires manifest tag off the abstract base; keep it on concrete
  components only, and place @Alpha before the final tag block

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Speed the selection transition up to 100ms ease-out. Suppress hover
feedback (via --obc-can-hover) from click until the next pointer move,
because browsers keep :hover on the thumb that shuffles away from under
a stationary pointer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The lit-labs wrapper generators resolve event types declared in the
component's own module; a type re-exported from the shared base is
emitted into the Vue wrapper without an import, breaking the vue and
demo builds. Declare PositionSelectedDetail locally in each valve
component (matching the progress-button pattern) and reference it from
the @fires tag.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

ludvigovrevik and others added 3 commits July 24, 2026 01:21
The generated React wrapper passes every manifest class to
createComponent as Constructor<T>; an abstract constructor is not
assignable. Same approach as the specialty-tank base: concrete class
with default implementations that subclasses override.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add a vertical boolean to the shuffle-button base and the hydraulic
valve components, per the Figma shuffle button Direction=Vertical
variants: positions stack top to bottom, the track and thumb geometry
transpose, and position symbols rotate 90deg counter-clockwise to match
a vertical flow path. Linux visual baselines generated via the project
Docker image and verified stable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant