diff --git a/nym-wallet/src/pages/families/FamilyFlows.stories.tsx b/nym-wallet/src/pages/families/FamilyFlows.stories.tsx
index 7d5787b2d4..50d84e3c73 100644
--- a/nym-wallet/src/pages/families/FamilyFlows.stories.tsx
+++ b/nym-wallet/src/pages/families/FamilyFlows.stories.tsx
@@ -1,3 +1,4 @@
+import React from 'react';
import type { Meta, StoryObj } from '@storybook/react-webpack5';
import { within, screen, userEvent, waitFor, expect } from 'storybook/test';
import { withFamiliesMock } from 'src/components/Families/withFamiliesMock';
@@ -11,6 +12,7 @@ import {
MOCK_OWNER_FLOW_NODE,
} from 'src/context/mocks/families.fixtures';
import { FamilyPage } from './FamilyPage';
+import { OperatorInvitesPage } from './OperatorInvitesPage';
/**
* End-to-end flow stories driven by play functions against the mock contract.
@@ -30,6 +32,7 @@ const NODE = MOCK_OWNER_FLOW_NODE;
/** Owner lifecycle: create → invite → accept → kick → disband (single self-controlled account). */
export const OwnerLifecycle: Story = {
+ name: 'Owner Lifecycle (auto-run)',
decorators: [withFamiliesMock({ sender: MOCK_OWNER_ADDRESS, makeStore: buildOwnerFlowStore, latencyMs: 0 })],
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
@@ -68,6 +71,7 @@ export const OwnerLifecycle: Story = {
/** Operator lifecycle: receive → accept (then leave) on one node, reject on another. */
export const OperatorLifecycle: Story = {
+ name: 'Operator Lifecycle (auto-run)',
decorators: [withFamiliesMock({ sender: MOCK_OPERATOR_ADDRESS, makeStore: buildOperatorFlowStore, latencyMs: 0 })],
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
@@ -90,3 +94,30 @@ export const OperatorLifecycle: Story = {
await canvas.findByTestId(`node-invite-group-${MOCK_OPERATOR_FLOW_REJECT_NODE}-empty`);
},
};
+
+// ---------------------------------------------------------------------------
+// Manual variants — same seeded scenarios, NO play function, so you can click
+// through the steps yourself (accept / reject / leave) and watch state change.
+// ---------------------------------------------------------------------------
+
+/**
+ * Operator lifecycle, driven by hand. Two controlled nodes each hold an active
+ * invite: accept node {ACCEPT}'s invite (a "Current family" card with Leave then
+ * appears) and reject node {REJECT}'s invite. Nothing runs automatically.
+ */
+export const OperatorLifecycleManual: Story = {
+ name: 'Operator Lifecycle (manual)',
+ decorators: [withFamiliesMock({ sender: MOCK_OPERATOR_ADDRESS, makeStore: buildOperatorFlowStore, latencyMs: 300 })],
+ render: () => ,
+};
+
+/**
+ * Owner lifecycle, driven by hand. Starts with no family (the create entry point);
+ * create one, then invite node {NODE} (which this account also controls), switch to
+ * the Node invites tab to accept it, kick it, and dissolve. Nothing runs automatically.
+ */
+export const OwnerLifecycleManual: Story = {
+ name: 'Owner Lifecycle (manual)',
+ decorators: [withFamiliesMock({ sender: MOCK_OWNER_ADDRESS, makeStore: buildOwnerFlowStore, latencyMs: 300 })],
+ render: () => ,
+};