Skip to content

Commit f62139f

Browse files
authored
Merge branch 'main' into revert/pr-20-joelpeace48-cell
2 parents 7240c93 + 448661c commit f62139f

36 files changed

Lines changed: 12940 additions & 8653 deletions
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
# BuyActionHelperText - Disabled Reason Feature
2+
3+
## Overview
4+
5+
The `BuyActionHelperText` component now supports displaying a disabled reason message when an action cannot be performed. This feature was added in issue-67 to provide users with clear feedback about why a market action is unavailable.
6+
7+
## Usage
8+
9+
### Basic Example
10+
11+
```tsx
12+
import BuyActionHelperText from '@/components/common/BuyActionHelperText';
13+
14+
<BuyActionHelperText
15+
state="idle"
16+
disabledReason="Insufficient balance to complete this purchase"
17+
className="mt-4"
18+
/>
19+
```
20+
21+
### With CreatorCard
22+
23+
```tsx
24+
const CreatorCard: React.FC<CreatorCardProps> = ({ creator, className }) => {
25+
const { isConnected } = useAccount();
26+
const [transactionState, setTransactionState] = useState<
27+
'idle' | 'submitting' | 'failed' | 'success'
28+
>('idle');
29+
30+
// Example: Check if user has sufficient balance
31+
const hasInsufficientBalance = true; // Replace with actual balance check
32+
const disabledReason = hasInsufficientBalance
33+
? 'Insufficient balance to complete this purchase'
34+
: undefined;
35+
36+
return (
37+
<div>
38+
{/* ... other card content ... */}
39+
40+
<BuyActionHelperText
41+
state={transactionState}
42+
disabledReason={disabledReason}
43+
className="mt-4"
44+
/>
45+
</div>
46+
);
47+
};
48+
```
49+
50+
## Props
51+
52+
| Prop | Type | Required | Description |
53+
|------|------|----------|-------------|
54+
| `state` | `'idle' \| 'submitting' \| 'failed' \| 'success'` | Yes | Current transaction state |
55+
| `className` | `string` | No | Additional CSS classes |
56+
| `disabledReason` | `string` | No | Reason message when action is disabled |
57+
58+
## Behavior
59+
60+
- **When `disabledReason` is provided**: Displays the reason text below the main state message with subtle styling (`text-white/40`)
61+
- **When `disabledReason` is empty/null/undefined**: Nothing renders (no empty space or placeholder)
62+
- **Animation**: Smooth fade-in/fade-out with height animation when reason appears/disappears
63+
64+
## Styling
65+
66+
The disabled reason text uses:
67+
- Font size: `0.72rem` (matching the main message)
68+
- Color: `text-white/40` (subtle, non-intrusive)
69+
- Animation: Framer Motion with opacity and height transitions
70+
- Layout: Stacked below the main message with `space-y-2` gap
71+
72+
## Examples of Disabled Reasons
73+
74+
```tsx
75+
// Insufficient balance
76+
disabledReason="Insufficient balance to complete this purchase"
77+
78+
// Network issue
79+
disabledReason="Network connection required to proceed"
80+
81+
// Wallet not connected
82+
disabledReason="Connect your wallet to enable purchases"
83+
84+
// Creator unavailable
85+
disabledReason="This creator is currently unavailable"
86+
87+
// Maximum keys reached
88+
disabledReason="Maximum number of keys already purchased"
89+
```
90+
91+
## Safe Empty Content Handling
92+
93+
The component safely handles all empty cases:
94+
95+
```tsx
96+
// All of these will render nothing for the disabled reason
97+
<BuyActionHelperText state="idle" disabledReason={undefined} />
98+
<BuyActionHelperText state="idle" disabledReason={null} />
99+
<BuyActionHelperText state="idle" disabledReason="" />
100+
<BuyActionHelperText state="idle" disabledReason=" "} /> // whitespace only
101+
<BuyActionHelperText state="idle" /> // prop not provided
102+
```
103+
104+
## Implementation Details
105+
106+
The component uses a conditional check:
107+
108+
```tsx
109+
const hasDisabledReason = disabledReason && disabledReason.trim().length > 0;
110+
```
111+
112+
This ensures:
113+
- Null/undefined values are handled
114+
- Empty strings are ignored
115+
- Whitespace-only strings are ignored
116+
- No empty elements are rendered
117+
118+
## Accessibility
119+
120+
- The disabled reason text is part of the same container as the main message
121+
- Screen readers will announce both the state message and disabled reason
122+
- Color contrast meets WCAG guidelines with `text-white/40` on dark backgrounds
123+
124+
## Complete Example
125+
126+
```tsx
127+
import { useState } from 'react';
128+
import { useAccount } from 'wagmi';
129+
import BuyActionHelperText from '@/components/common/BuyActionHelperText';
130+
import { Button } from '@/components/ui/button';
131+
132+
export function BuyActionExample() {
133+
const { isConnected } = useAccount();
134+
const [transactionState, setTransactionState] = useState<
135+
'idle' | 'submitting' | 'failed' | 'success'
136+
>('idle');
137+
138+
// Example conditions that might disable an action
139+
const userBalance = 0.5; // ETH
140+
const requiredAmount = 1.0; // ETH
141+
const isNetworkAvailable = true;
142+
const hasReachedLimit = false;
143+
144+
// Determine disabled reason based on conditions
145+
const getDisabledReason = () => {
146+
if (!isConnected) {
147+
return 'Connect your wallet to enable purchases';
148+
}
149+
if (userBalance < requiredAmount) {
150+
return `Insufficient balance. You need ${requiredAmount - userBalance} ETH more`;
151+
}
152+
if (!isNetworkAvailable) {
153+
return 'Network connection required to proceed';
154+
}
155+
if (hasReachedLimit) {
156+
return 'Maximum number of keys already purchased';
157+
}
158+
return undefined; // No disabled reason
159+
};
160+
161+
const disabledReason = getDisabledReason();
162+
const isDisabled = !!disabledReason || transactionState === 'submitting';
163+
164+
return (
165+
<div className="space-y-4">
166+
<Button
167+
onClick={() => setTransactionState('submitting')}
168+
disabled={isDisabled}
169+
variant={isConnected ? 'default' : 'outline'}
170+
>
171+
Buy Key
172+
</Button>
173+
174+
<BuyActionHelperText
175+
state={transactionState}
176+
disabledReason={disabledReason}
177+
className="mt-4"
178+
/>
179+
</div>
180+
);
181+
}
182+
```

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
"check": "pnpm lint && pnpm build",
1616
"prepare": "husky",
1717
"lint-staged": "lint-staged",
18+
"test": "vitest run",
19+
"test:watch": "vitest",
1820
"preview": "vite preview",
1921
"typechain": "typechain --target ethers-v6 --out-dir src/types/contracts \"src/contracts/abis/*.json\""
2022
},
@@ -75,6 +77,7 @@
7577
"typechain": "^8.3.2",
7678
"typescript": "~5.8.3",
7779
"typescript-eslint": "^8.35.1",
78-
"vite": "^7.0.4"
80+
"vite": "^7.0.4",
81+
"vitest": "^4.1.2"
7982
}
8083
}

0 commit comments

Comments
 (0)