Skip to content

Add SpecResults: run status pills + progress bar (React) - #719

Open
ryanjwilke wants to merge 50 commits into
mainfrom
spec-results-component
Open

Add SpecResults: run status pills + progress bar (React)#719
ryanjwilke wants to merge 50 commits into
mainfrom
spec-results-component

Conversation

@ryanjwilke

@ryanjwilke ryanjwilke commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

What & why

Cypress Cloud's Run Overview page had a design-exploration prototype
(SpecsStatusStripPrototype.tsx, cypress-services PR #14230) replacing the
old NoticeSpecsQueuedRunning "well" notice — a row of per-status pills
(failed/errored/passed/skipped/remaining), each linking to the Specs tab
filtered to that status, plus a proportional tick bar along the bottom edge.
This promotes that prototype into a real, versioned @cypress-design/*
component so it can replace the legacy notice everywhere (Overview, Specs,
and Test Results tabs) instead of living as throwaway page-local code.

Ported directly from the prototype, with one addition: a "scheduled to
complete" state (a trailing "60s remaining" pill + link to project settings)
for a run held open by project.runCompletionDelay after every group
finishes — the prototype didn't have this, the old NoticeSpecsQueuedRunning
did.

How

  • components/SpecResults/constants (@cypress-design/constants-spec-results,
    private) — the status model, class tables, and the one real algorithm,
    buildSpecResultsView() (pill list + tick-bar groups, indeterminate /
    running / scheduled-to-complete / complete states).
  • components/SpecResults/react (@cypress-design/react-spec-results) —
    thin render layer: pills render as <a> tags and, when mounted inside a
    react-router Router, navigate client-side via useNavigate (plain
    anchors otherwise); @cypress-design/react-statusicon for icons,
    @cypress-design/react-button for the Cancel / Archive actions.
  • React + constants only for now — Vue is a deliberate fast-follow (noted in
    architecture.md), since the only current consumer (cypress-services'
    dashboard) is React-only.
  • Documented as a normal Component page (/components/SpecResults): the
    existing [component].astro pipeline picks up the React-only package as-is
    and renders react/ReadMe.md, instructions.md, and a live demo
    (docs/src/demos/SpecResults.astro, one React island backed by
    SpecResultsDemo.tsx). An earlier revision shipped this as a hand-built
    Pattern page; reverted per review.
  • SpecResultCounts accepts skipped and cancelled as separate inputs
    (matching Cloud's NOTESTS vs CANCELLED instance statuses) that the
    component itself sums into one "skipped" pill and one combined Specs-tab
    filter link — callers don't pre-sum these.
  • Every spacing/sizing value is a literal [Npx] arbitrary value rather than
    Tailwind's rem-based scale (h-6, text-base, ...) — a workaround for a
    bug in the one current consumer (cypress-services's dashboard still sets
    the page's root font-size to 10px via a legacy bootstrap-sass import),
    not a flaw in this component or design system. Documented as a known issue
    in architecture.md with an explicit "don't switch back without
    confirming the root cause is fixed" warning.
  • Cypress component tests (SpecResultsReact.cy.tsx) cover every documented
    state: indeterminate, running (partial/all-passing/queued-only), scheduled
    to complete, complete (passed/failed/errored), label="", and the
    skipped+cancelled combination.

Where to focus review

  • buildSpecResultsView() in constants/src/index.ts — this is where all
    the actual state logic lives; the React file is deliberately thin.
  • The href scheme (specs?specStatus=..., ../../settings/general) — every
    href is relative to a Cypress Cloud run tab's own URL
    (/projects/:id/runs/:runId/<tab>); architecture.md has the reasoning
    and a worked example for why specs has no leading ../ while the
    settings link needs ../../.
  • Whether the px-not-rem workaround (architecture.md, "Known issue"
    section) is the right call, versus fixing the root font-size bug in
    cypress-services directly first.

Note

Medium Risk
New major UI surface for Cloud run pages with routing, filter URLs, and consumer-specific Tailwind workarounds; icon SVG changes affect all StatusIcon consumers.

Overview
Introduces @cypress-design/react-spec-results (with private @cypress-design/constants-spec-results) as the versioned replacement for Cypress Cloud’s run-overview status strip: filterable per-status pills, proportional tick bar, optional Cancel / Archive, a description slot, run-completion-delay (“scheduled to complete”) behavior, and buildSpecResultsView() holding the state logic. Pills use client-side navigation when a React Router context exists; otherwise they stay plain anchors. Docs add component listing, agent index entry, architecture notes, a live demo, and broad Cypress coverage.

Also insets the errored outline status icon circle (changeset patches icon-registry / StatusIcon packages), adds action-archive to the icon registry for Archive, documents the dist/ rebuild gotcha for local component work, and adds react-router-dom as a dependency of the new package.

Reviewed by Cursor Bugbot for commit 4a1121a. Bugbot is set up for automated code reviews on this repo. Configure here.

ryanjwilke and others added 20 commits September 2, 2026 02:47
A run's per-status spec breakdown (failed/errored/passed/skipped/
remaining) as filterable pills plus a proportional progress bar,
replacing the old NoticeSpecsQueuedRunning well pattern.

Ported from a live design-iteration prototype in cypress-services
(frontend/packages/dashboard/src/specs/SpecsStatusStripPrototype.tsx,
PR #14230), plus one new capability that prototype didn't have: a
"scheduled to complete" state for a run held open by the project's
completion delay.

React + constants only for now -- Vue is a planned fast-follow.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ainer query

The instructions.md language was copied from the reference web-component
implementation (which used container queries via Shadow DOM's container-type),
but the actual React component uses Tailwind's sm: viewport breakpoint.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The reference material this was ported from already framed it this way
("Patterns · Cypress Cloud"), which got lost when it was first filed
under the flat Components list. Unlike Card/Button Bar/Test Results
List, SpecResults keeps a real installable package underneath (its
state-derivation logic isn't a copy-paste recipe) -- getAllComponents()
now excludes it from the auto-generated Components index via a small
PATTERN_ONLY allowlist, while the package itself still lives under
components/SpecResults/ where the build/publish tooling expects it.

Also adds a live docs/src/demos/SpecResults.astro demo (client:only=
"react") so the pattern page actually renders the component instead of
just showing code -- this component had no live preview at all before.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The flat Usage+code-block template shared by Card/Button Bar/Test
Results List undersold this one -- the reference material's own intent
was closer to a foundational page like /colors: an eyebrow, a big
headline with an italic accent, a lede, and stat badges, ahead of
the actual sections.

Reuses the exact Tailwind classes /colors/index.astro already
establishes for that hero treatment (font-brand-heading, the
eyebrow/headline/lede sizing) rather than introducing new bespoke
CSS, so this stays visually consistent with the one other page that
already uses this pattern in the real site.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The previous pass ("inspired by /colors") was too thin -- a bare hero
plus flat Usage+code-block sections. This replaces it with a full
custom Astro page (docs/src/pages/patterns/spec-results.astro,
converted from .mdx since MDX can't do the arbitrary layout control
this needs) that actually reproduces the reference material's
structure: Basic usage, a 9-state Component states stack with
captioned boxes, an API props table, an Interaction table, When to
use it, and Guidelines -- with a right-side "On this page" TOC via
BaseLayout's tocHeadings, matching the reference page's own outline
exactly.

Two real bugs found and fixed along the way, not just layout:

- Astro's client:only islands serialize props to JSON before
  hydrating, so a function passed as onCancel from the .astro
  frontmatter silently became null and every Cancel button in the
  demos disappeared. Added SpecResultsCancelable.tsx, a thin wrapper
  that defines the callback inside the hydrated island instead of
  passing it in from outside -- the standard workaround for this
  Astro limitation. Confirmed via the actual serialized
  astro-island props attribute before and after.
- The reference eyebrow ("Patterns · Cypress Cloud") used
  letter-spacing: 2px that got dropped when the hero was first built
  from /colors' plain eyebrow class; restored via tracking-[2px].

Also fixes Sidebar.astro's pattern-page glob (previously .mdx only)
to pick up .astro pattern pages too, and gives the astro:components
Code block real padding/font-size/rounding -- it renders bare by
default with none of that.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
h-5 + inline-flex items-center to fix the height instead of letting
line-height/py-0.5 set it, and text-purple-500 to match the token
color used elsewhere on the page (package-name pill, prop types).
Applies to all 15 inline <code> references across the page.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
At px-8 padding the row needed ~722px but only had ~706px available
-- a ~16px shortfall that tripped flex-wrap and pushed Cancel run
onto its own line. Dropping to px-6 reclaims exactly enough width;
confirmed via the pills/button now sharing the same row offset.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Cypress Cloud (cypress-services' dashboard) still vendors bootstrap-
sass, which sets html { font-size: 10px } globally instead of the
standard 16px -- so every rem-based Tailwind utility renders at 62.5%
of its documented value there (gap-2, nominally 8px, measures 5px).
Confirmed live in that dashboard via getComputedStyle after this fix:
pill height 24px, gap 6px, font-size 16px, strip height 56px -- all
correct now, versus visibly compressed before.

Converts every spacing/sizing class in CssClasses (constants/src/
index.ts) and the Cancel button's gap in react/SpecResults.tsx to
literal [Npx] arbitrary values. rounded/rounded-b/rounded-tl-none/
rounded-tr-none are left as-is -- this design system's own theme
hardcodes borderRadius.DEFAULT/.md to a literal 4px rather than a rem
value, so those were never affected.

Documented as a known issue in architecture.md and instructions.md:
this is a workaround for that one consumer's bug, not a flaw in this
component or in the design system's own Tailwind preset. A consumer
with a normal 16px root renders [24px] and h-6 identically either
way.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
NOTESTS and CANCELLED are distinct RunInstanceStatusEnum values, so callers
should not have to pre-sum them. skipped/cancelled now combine into one
"skipped" pill and one Specs-tab filter link, same as before, but the
component does the summing instead of the caller.
- Props table's first column matches Type/Default's text-sm (was
  inheriting the base 16px instead).
- Every table's first/last cell drops its outer-edge padding (left on
  the first column, right on the last) now that it sits flush against
  the table's own border.
- Props table header cells use the Poppins heading font and heading
  color instead of the small gray label treatment.
Replace the 4-column Prop/Type/Default/Description table with a
per-prop stack: the prop name as a code label, then Description/Type/
(Default when one exists) rows underneath. Reads better for long
description text than a table cell ever did.
Matches the referenced Atlassian-style layout: a hairline directly
under the prop-name chip, not just between the Description/Type/
Default rows underneath it.
text-base -> text-sm on the prop-name label, and mb-10 -> mb-16
between each prop's stack.
Every run tab (including Specs itself) is already a path segment under
runs/:id/, so "../specs" resolves one level too far up and drops the
run id -- e.g. from .../runs/456/overview it lands on .../runs/specs
instead of .../runs/456/specs. Verified with node's URL resolution.
The scheduled-to-complete pill's ../../settings/general was already
correct (it genuinely needs to climb out of runs/:id/ to the project
root) and is untouched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Sep 2, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 4a1121a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 8 packages
Name Type
@cypress-design/icon-registry Patch
@cypress-design/constants-statusicon Patch
@cypress-design/react-statusicon Patch
@cypress-design/vue-statusicon Patch
@cypress-design/constants-spec-results Major
@cypress-design/react-spec-results Major
@cypress-design/react-icon Patch
@cypress-design/vue-icon Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Sep 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
cypress-design Ready Ready Preview Sep 5, 2026 7:28am UTC

Request Review

Comment thread components/SpecResults/constants/src/index.ts Outdated
@cypress

cypress Bot commented Sep 2, 2026

Copy link
Copy Markdown

cypress-design    Run #4140

Run Properties:  status check passed Passed #4140  •  git commit 4e729f6205 ℹ️: Merge 4a1121a3e123c4776b19f948c9e521b8c2f10688 into db11411a28f7d3f9690e11bfee3d...
Project cypress-design
Branch Review spec-results-component
Run status status check passed Passed #4140
Run duration 02m 12s
Commit git commit 4e729f6205 ℹ️: Merge 4a1121a3e123c4776b19f948c9e521b8c2f10688 into db11411a28f7d3f9690e11bfee3d...
Committer Ryan Wilke
View all properties for this run ↗︎

Test results
Tests that failed  Failures 0
Tests that were flaky  Flaky 0
Tests that did not run due to a developer annotating a test with .skip  Pending 0
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 2
View all changes introduced in this branch ↗︎

Warning

No Report: Something went wrong and we could not generate a report for the Application Quality products.

sm:h-[56px] was a fixed height, so a run with enough pills + Cancel run
to wrap onto a third line had that line clipped/overlapping the tick
bar instead of the container growing to fit. sm:min-h-[56px] keeps the
usual single-line height while letting wrapped content push it taller.
sm:pt-0 sm:pb-[4px] zeroed out the base pt-[12px]/pb-[16px] at desktop
width, relying on the old fixed sm:h-[56px] + justify-center to
center a single line without needing padding. Now that height is
min-h-[56px] (so wrapped content can grow the container), that trick
left wrapped content flush against the top/bottom edges with no
breathing room. Dropping the sm: overrides restores real padding at
every breakpoint; justify-center still centers a single line fine
since 12+24+16=52px is under the 56px minimum.

Also trims the docs page's "Basic usage" demo box from py-8 to py-6
now that the component itself carries real padding.
…iner query

sm: is a viewport media query, so a strip embedded somewhere narrower
than the browser window (a sidebar, a split pane) would never stack
even though its own rendered width can't fit the row layout. The
strip now declares itself a query container (@container) and its
children key off @[576px]: -- an arbitrary-value container variant,
not the @tailwindcss/container-queries plugin's named @sm:/@XL:
scale, since that scale is rem-based and would hit the exact
root-font-size bug this component already works around everywhere
else (see the "Known issue" note in architecture.md). 576px is the
plugin's xl step evaluated at a normal 16px root -- the closest named
container breakpoint to the ~600px threshold, kept as a literal px
value instead.

Both cypress-design's own Tailwind preset and cypress-services'
already register @tailwindcss/container-queries (confirmed in
cypress-services' tailwind.config.cjs via the shared TailwindConfig()
preset), so no consumer-side setup is needed.

Adds a regression test mounting the strip in a narrow (300px)
fixed-width wrapper at Cypress's default (wide) viewport, asserting
it stacks -- the exact case a viewport media query would get wrong.
A raw stroke="#..." attribute on the source SVG has the lowest
possible CSS priority, so every consumer's own auto-generated
icon-light-{color} class (from constants-statusicon's per-status
config) silently overrode it -- the on-page color never actually
changed, confirmed via getComputedStyle after the fact. Only the
stroke-opacity edits were ever real, since there's no class rule
setting that property. Reverting the color hack entirely; the real,
per-surface fix lives in SpecResults itself (next commit), via
className overrides rather than editing a shared icon's own default.
The real fix for the previous commit's problem: instead of touching a
shared icon's own hardcoded default, override className per call site
with the DS's existing icon-{light,dark}-{color} class system --
!icon-light-gray-200 for running on the strip's white background,
!icon-light-gray-700 for running inside a dark tooltip row,
!icon-dark-gray-700 for queued (single icon-dark-only ring) in the
same dark row. Also darkens the Auto Cancellation lightning bolt one
shade (indigo-300 stroke, was indigo-200) so the stroke isn't the
brightest element in the row.

Also:
- Tooltip text bumped to 14px/20px, matching RunResults' own tooltip
  convention (the shared Tooltip's bare default, 16px/24px, is sized
  for a single short label).
- Scheduled-to-complete tooltip's body copy tightened to match the
  exact wording requested, and its CTA shortened from "Update settings"
  to "Update setting" (singular -- one setting, not a page section).

Docs: new architecture.md section covering the per-surface override
pattern and -- explicitly -- the raw-attribute mistake it replaces,
including how to verify a color fix actually landed
(getComputedStyle, not getAttribute).
react-spec-results/package.json gained two new dependencies this PR
(@cypress-design/react-tooltip, @cypress-design/react-icon) but the
root lockfile was never regenerated to match. CI's --immutable
install correctly refused to proceed rather than silently modify a
lockfile it wasn't told to -- failing Build and Vercel both, since
both run the same install step.
@[576px]:min-h-[56px] was on the same element as @container -- a
container query can only be consumed by a container's descendants,
never by the element that establishes @container itself. Confirmed
via getComputedStyle: it computed min-height: 0px at every width,
silently never matching (caught by Cursor Bugbot, PR #719).

Made it unconditional instead of moving @container to a wrapper: a
min-height floor is harmless at every width (it only has any visible
effect on the single-line desktop layout, where natural content
height can be under 56px -- the stacked/narrow layout's natural
height already exceeds it, so the floor is a no-op there). Verified
both widths directly: 700px reads min-height 56px (previously 0px);
300px reads min-height 56px with actual content height 206px, i.e.
no narrow-width regression.
…ading JQuery

Spreading a JQuery<HTMLElement> collection requires downlevelIteration under
this tsconfig's target; .toArray() is jQuery's own array conversion and
needs no iteration protocol at all.
…icon

New SpecResultsWithDescription/SpecResultsArchivable demo wrappers and the
IconActionArchive source SVG, covering timed-out/errored/cancelled/no-tests
states for the docs site.
…tooltip

isComplete lets a caller mark a timed-out/abandoned run as complete even
though it still carries a nonzero queued count, so Archive can show where
the derived state alone would hide it.

The remaining pill's tooltip now always breaks running/queued apart, same
as the skipped pill already does -- "N specs remaining" doesn't say which
state those specs are actually in, so the reason is worth naming even when
only one cause contributes.
- Add isComplete and the missing spec-results-archive selector/Archive run
  row to instructions.md and the docs Props/Interaction tables; mention
  Archive in the accessibility section
- Fix the remaining-pill tooltip docs to match the always-render behavior
- Drop the redundant "mirrors onCancel" aside from onArchive's description
  now that it's documented as its own thing
- Standardize on "cancelled" (not "canceled") across the component's docs
  and demo copy
- Fix the manually-cancelled demo's timestamp: no gap before the period,
  medium weight, real timestamp as a title tooltip

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 14320c7. Configure here.

Comment thread components/SpecResults/react/SpecResults.tsx Outdated
Comment thread components/SpecResults/react/SpecResults.tsx Outdated
…fset

isComplete now also requires !scheduledToComplete -- a run held open by
the completion delay had nothing running or queued, so it read as
complete and showed Archive next to the live countdown pill. Splits the
old isComplete check into allSpecsFinished (still gates the
scheduled-to-complete pill/tick, unchanged) and isComplete (now excludes
the delay window).

The first pill's -ml-[6px] offset (canceling its own px-[6px] to align
with the description text above) only applied via index===0, so any pill
that started a second row -- stacked below 576px, or wrapped above it --
kept its padding and sat 6px right of where it should. Moved the offset
onto the pills container itself so every row gets it.
border-b/border-t alone only set border-width; they render nothing
without a border-style, which this component was relying on Tailwind
preflight's *,::before,::after default (border-style: solid) to supply.
cypress-services' own build doesn't carry that preflight declaration, so
the description divider and the two button-row dividers rendered with
zero visible border despite computing a width and color. The outer strip
card's own border already did this correctly (border border-solid); the
three newer dividers didn't match it.
…utility

The named border-solid utility set border-style: solid unscoped (all four
sides), and since it's already used elsewhere in cypress-services' own
source, adding it here also affected how that utility renders on
unrelated elements already carrying the class -- confirmed by diffing a
build with and without this change: an unrelated Button's uniform 1px
border became uneven (1.5px on three sides) purely from this component
picking up the same class name.

Replaced with a directional arbitrary-value selector
([border-bottom-style:solid] / [border-top-style:solid]) instead --
these compile to a selector no other class name can coincide with, so
they can only ever affect this component's own three dividers.
cypress-services doesn't supply Tailwind's border-style: solid preflight
default, so a bare border-b/border-t renders no visible border there, and
the obvious first fix (the named border-solid utility) caused a real
regression on an unrelated component. Tracked as PD-42 to fix at the
actual source instead of every component needing this workaround.
The circle-slash "skipped" icon was only ever a stand-in (no dedicated
cancel icon exists in the design system yet); it read as a status glyph
rather than a cancel action, so drop it and let the button's text carry
the label. Also documents the dist/-vs-source gotcha this fix ran into
while iterating locally, so it's not rediscovered the hard way again.
Cypress Cloud is the only real consumer of this component right now, so
depend on react-router-dom directly rather than adding a generic
link-injection prop for a hypothetical non-router consumer that doesn't
exist yet. Clicking a pill was doing a full browser navigation (reload
the document, re-fetch and re-execute the whole bundle) even though the
Specs/Test Results tabs it links to already live in the same app shell.

Deliberately not react-router-dom's own <Link to="..."> for the
navigation itself: v6 resolves a relative `to` against the full current
URL (including whatever a wildcard tab route already consumed), so from
"/runs/6/specs" it turned "specs?..." into the nonsense
"/runs/6/specs/specs?...". A real <a href> doesn't have that problem --
same relative resolution any browser link uses -- so this keeps the
authored href as-is, lets the browser's own URL resolution compute the
target, then hands that to useNavigate() instead of letting the click
fall through to a real navigation.

Falls back to an unmodified plain anchor outside a Router (this
component's own docs demos render with none) -- useInRouterContext is
safe to call unconditionally, but the hooks that do real navigation work
throw outside a Router, so the conditional branches on which component
gets mounted rather than which hooks get called.
@ryanjwilke ryanjwilke changed the title Add SpecResults: run status pills + progress bar (React, Pattern) Add SpecResults: run status pills + progress bar (React) Sep 5, 2026
Per review: a pattern is one component shown in its various uses across
applications; SpecResults is itself a component. The existing
docs/src/pages/components/[component].astro pipeline already handles a
React-only package -- it derives the framework list from react/ existing,
hides the framework tabs for a single framework, renders react/ReadMe.md +
instructions.md, and accepts an optional .astro live demo -- so the
hand-built pattern page and its plumbing are unnecessary.

- Revert docs/src/lib/components.ts (PATTERN_ONLY) and
  docs/src/components/Sidebar.astro (patterns *.astro glob) to main.
- Delete docs/src/pages/patterns/spec-results.astro and the three .tsx
  island wrappers that lived under pages/ (Astro warned "Unsupported file
  type ... found in pages directory" for each on every build).
- Add docs/src/demos/SpecResults.astro: a single client:only="react" island
  backed by SpecResultsDemo.tsx, which renders the same 14 labelled states
  the pattern page had. Callbacks and description nodes are created inside
  the island, so no per-callback wrapper files are needed. The manual-cancel
  example no longer hot-links a third-party avatar service.
- .agents/index.md: drop the Patterns section (its Card / Button Bar /
  Test Results List instructions.md URLs were never published by
  copy-agents.mjs) and list SpecResults under Components.
- architecture.md / instructions.md: the auto-render was never blocked on a
  Vue demo; say what the component page actually renders today.

Co-Authored-By: Claude Fable 5.1 <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