Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
"dependencies": {
"@stellar-scaffold/app-lib": "*",
"@tanstack/react-query": "^5.90.17",
"@theahaco/contract-explorer": "^1.3.0",
"@theahaco/ts-config": "^1.3.0",
"lossless-json": "^4.3.0",
"react": "^19.2.6",
Expand Down
25 changes: 1 addition & 24 deletions app/src/App.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
.header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 2rem;
padding: 1rem 3rem;
border-bottom: 1px solid var(--color-border);
Expand All @@ -20,30 +21,6 @@
white-space: nowrap;
}

.headerNav {
display: flex;
gap: 0.5rem;
flex: 1;
}

.headerNav a {
color: var(--color-text-muted);
text-decoration: none;
padding: 0.25rem 0.5rem;
border-radius: var(--radius);
font-size: 0.9rem;
}

.headerNav a:hover {
color: var(--color-text);
background: var(--color-surface);
}

.headerNav a.active {
color: var(--color-text);
font-weight: 500;
}

.main {
flex: 1;
padding: 2rem 3rem;
Expand Down
27 changes: 2 additions & 25 deletions app/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
import { labPrefix } from "@stellar-scaffold/app-lib"
import { NavLink, Outlet, Route, Routes } from "react-router-dom"
import { Outlet, Route, Routes } from "react-router-dom"
import styles from "./App.module.css"
import ConnectAccount from "./components/ConnectAccount"
import Debug from "./pages/Debug"
import Home from "./pages/Home"
import VaultPreview from "./pages/VaultPreview"

function App() {
return (
<Routes>
<Route element={<AppLayout />}>
<Route path="/" element={<Home />} />
<Route path="/debug" element={<Debug />} />
<Route path="/debug/:contractName" element={<Debug />} />
<Route path="/preview" element={<VaultPreview />} />
<Route path="/" element={<VaultPreview />} />
</Route>
</Routes>
)
Expand All @@ -23,23 +17,6 @@ const AppLayout = () => (
<div className={styles.AppLayout}>
<header className={styles.header}>
<span className={styles.logo}>Strata Vault Kit</span>
<nav className={styles.headerNav}>
<NavLink
to="/debug"
className={({ isActive }) => (isActive ? styles.active : "")}
>
Contract Explorer
</NavLink>
<NavLink
to="/preview"
className={({ isActive }) => (isActive ? styles.active : "")}
>
Component Preview
</NavLink>
<a href={labPrefix()} target="_blank" rel="noreferrer">
Transaction Explorer
</a>
</nav>
<ConnectAccount />
</header>

Expand Down
15 changes: 12 additions & 3 deletions app/src/components/vault/LifecyclePanel.module.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.panel {
container-type: inline-size;
display: flex;
flex-direction: column;
gap: 18px;
Expand All @@ -25,7 +26,8 @@

.steps {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
grid-auto-flow: column;
grid-auto-columns: minmax(0, 1fr);
gap: 16px;
margin: 0;
padding: 0;
Expand Down Expand Up @@ -84,6 +86,10 @@
background: var(--vault-action);
}

.step:last-child .connector {
display: none;
}

.titleActive {
color: var(--vault-ink);
}
Expand All @@ -110,9 +116,12 @@
color: var(--vault-text-muted);
}

@media (max-width: 1080px) {
/* The panel's own width, not the window's: it sits in the main column, whose
content box is around 700px on a desktop. A threshold near that never lets
the steps run across. */
@container (max-width: 560px) {
.steps {
grid-template-columns: minmax(0, 1fr);
grid-auto-flow: row;
}

.connector {
Expand Down
16 changes: 16 additions & 0 deletions app/src/components/vault/LifecyclePanel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,20 @@ describe("LifecyclePanel", () => {
items.every((item) => item.getAttribute("aria-current") === null),
).toBe(true)
})

it("marks no step as current when currentStep is past the last step", () => {
render(
<LifecyclePanel
title="Subscription lifecycle"
progress="Claimed"
steps={steps}
currentStep={4}
/>,
)

const items = screen.getAllByRole("listitem")
expect(
items.every((item) => item.getAttribute("aria-current") === null),
).toBe(true)
})
})
24 changes: 12 additions & 12 deletions app/src/components/vault/LifecyclePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,22 @@ export type LifecycleStep = {
body: string
}

export type LifecycleProgress = number | "complete" | null

type LifecyclePanelProps = {
title: string
progress: string
steps: [LifecycleStep, LifecycleStep, LifecycleStep]
currentStep: 1 | 2 | 3 | null
steps: LifecycleStep[]
currentStep: LifecycleProgress
}

type StepState = "done" | "current" | "upcoming"

const stepState = (
position: number,
currentStep: 1 | 2 | 3 | null,
currentStep: LifecycleProgress,
): StepState => {
if (currentStep === "complete") return "done"
if (currentStep === null) return "upcoming"
if (position < currentStep) return "done"
if (position === currentStep) return "current"
Expand All @@ -44,7 +47,6 @@ const LifecyclePanel: React.FC<LifecyclePanelProps> = ({
{steps.map((step, index) => {
const position = index + 1
const state = stepState(position, currentStep)
const showConnector = index < 2

return (
<li
Expand All @@ -56,14 +58,12 @@ const LifecyclePanel: React.FC<LifecyclePanelProps> = ({
<span className={`${styles.marker} ${styles[state]}`}>
{position}
</span>
{showConnector && (
<span
aria-hidden="true"
className={`${styles.connector} ${
state === "done" ? styles.connectorDone : ""
}`}
/>
)}
<span
aria-hidden="true"
className={`${styles.connector} ${
state === "done" ? styles.connectorDone : ""
}`}
/>
</div>
<span
className={`${typeStyles.stepTitle} ${
Expand Down
30 changes: 30 additions & 0 deletions app/src/components/vault/PositionCard.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
.card {
display: flex;
flex-direction: column;
gap: 12px;
padding: 22px;
border: 1px solid var(--vault-ink);
background: var(--vault-surface-card);
}

.label {
color: var(--vault-text-muted);
}

.figure {
display: flex;
flex-direction: column;
gap: 4px;
}

.value {
color: var(--vault-ink);
}

.sub {
color: var(--vault-text-muted);
}

.note {
color: var(--vault-text-muted);
}
11 changes: 11 additions & 0 deletions app/src/components/vault/PositionCard.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { render, screen } from "@testing-library/react"
import { describe, expect, it } from "vitest"
import PositionCard from "./PositionCard"

describe("PositionCard", () => {
it("renders an em dash when the value could not be read", () => {
render(<PositionCard label="Your shares" value={null} sub="Not read" />)

expect(screen.getByText("—")).toBeTruthy()
})
})
30 changes: 30 additions & 0 deletions app/src/components/vault/PositionCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from "react"
import typeStyles from "../../styles/type.module.css"
import styles from "./PositionCard.module.css"

export type PositionCardProps = {
label: string
value: string | null
sub: string
note?: string
}

const PositionCard: React.FC<PositionCardProps> = ({
label,
value,
sub,
note,
}) => (
<div className={styles.card}>
<span className={`${typeStyles.label} ${styles.label}`}>{label}</span>
<div className={styles.figure}>
<span className={`${typeStyles.position} ${styles.value}`}>
{value ?? "—"}
</span>
<span className={`${typeStyles.railValue} ${styles.sub}`}>{sub}</span>
</div>
{note && <p className={`${typeStyles.footnote} ${styles.note}`}>{note}</p>}
</div>
)

export default PositionCard
Loading