@streamoid/ui 0.6.16 → 0.6.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -18
- package/dist/docs/AGENTS.md +321 -0
- package/dist/docs/CreditWarningBanner.md +305 -0
- package/dist/docs/InvoiceHistoryMobile.md +222 -0
- package/dist/docs/ScAccess.md +259 -0
- package/dist/docs/ScAppCard.md +244 -0
- package/dist/docs/ScAppCardForCopilot.md +230 -0
- package/dist/docs/ScAppCardV3.md +273 -0
- package/dist/docs/ScAppField.md +308 -0
- package/dist/docs/ScAppListingCard.md +271 -0
- package/dist/docs/ScAppSwitchPanel.md +286 -0
- package/dist/docs/ScAppcardLogos.md +226 -0
- package/dist/docs/ScArtifaxInvite.md +262 -0
- package/dist/docs/ScArtifaxSidebar.md +330 -0
- package/dist/docs/ScAskAgentButton.md +307 -0
- package/dist/docs/ScBadges.md +261 -0
- package/dist/docs/ScBeacon.md +244 -0
- package/dist/docs/ScBillingHistoryHeader.md +210 -0
- package/dist/docs/ScBillingHistoryTableList.md +243 -0
- package/dist/docs/ScBillingLogsTableHeader.md +212 -0
- package/dist/docs/ScBillingLogsTableList.md +251 -0
- package/dist/docs/ScBriefCard.md +255 -0
- package/dist/docs/ScButton.md +251 -0
- package/dist/docs/ScCalendar.md +268 -0
- package/dist/docs/ScCalendarDateComps.md +264 -0
- package/dist/docs/ScCatalogixInvite.md +345 -0
- package/dist/docs/ScCatalogixSidebar.md +337 -0
- package/dist/docs/ScCatalogixStoreHeader.md +246 -0
- package/dist/docs/ScCatalogixStoreTableList.md +316 -0
- package/dist/docs/ScCheckField.md +233 -0
- package/dist/docs/ScCheckbox.md +272 -0
- package/dist/docs/ScCounter.md +235 -0
- package/dist/docs/ScCreditsUsageCard.md +247 -0
- package/dist/docs/ScCreditsUsageCardMobile.md +224 -0
- package/dist/docs/ScDefaultCard.md +269 -0
- package/dist/docs/ScDp.md +245 -0
- package/dist/docs/ScDrawer.md +318 -0
- package/dist/docs/ScFieldButton.md +255 -0
- package/dist/docs/ScFileField.md +268 -0
- package/dist/docs/ScGoogleSignIn.md +250 -0
- package/dist/docs/ScGuide.md +278 -0
- package/dist/docs/ScHDivider.md +213 -0
- package/dist/docs/ScHeader.md +222 -0
- package/dist/docs/ScImageField.md +253 -0
- package/dist/docs/ScInChatList.md +277 -0
- package/dist/docs/ScInChatMessage.md +205 -0
- package/dist/docs/ScInfoPopup.md +248 -0
- package/dist/docs/ScIntialProfileCover.md +233 -0
- package/dist/docs/ScInvoiceHistoryMobile.md +187 -0
- package/dist/docs/ScLogoUnit.md +232 -0
- package/dist/docs/ScMappingCard.md +241 -0
- package/dist/docs/ScMediaApproval.md +301 -0
- package/dist/docs/ScMediaSelect.md +310 -0
- package/dist/docs/ScMenuOptions.md +308 -0
- package/dist/docs/ScMobileBottomAction.md +252 -0
- package/dist/docs/ScMobileTopNav.md +279 -0
- package/dist/docs/ScModal.md +291 -0
- package/dist/docs/ScOnlyField.md +302 -0
- package/dist/docs/ScOnlyIcon.md +213 -0
- package/dist/docs/ScPagination.md +284 -0
- package/dist/docs/ScPairtext.md +287 -0
- package/dist/docs/ScPendingAction.md +238 -0
- package/dist/docs/ScPhtogenixInvite.md +275 -0
- package/dist/docs/ScPlanCard.md +302 -0
- package/dist/docs/ScPlanComparison.md +264 -0
- package/dist/docs/ScPlanDetailsCard.md +246 -0
- package/dist/docs/ScPlanDetailsCardMobile.md +240 -0
- package/dist/docs/ScPopUpMenu.md +224 -0
- package/dist/docs/ScProfile.md +234 -0
- package/dist/docs/ScProfileImageUpdate.md +261 -0
- package/dist/docs/ScProfileOptions.md +245 -0
- package/dist/docs/ScProfilePopup.md +396 -0
- package/dist/docs/ScProfileSettingsComp.md +250 -0
- package/dist/docs/ScProfileV2Mobile.md +216 -0
- package/dist/docs/ScProgressBar.md +267 -0
- package/dist/docs/ScQuickPrompt.md +277 -0
- package/dist/docs/ScRadio.md +228 -0
- package/dist/docs/ScReferralCardMobile.md +226 -0
- package/dist/docs/ScReferralTableHeader.md +260 -0
- package/dist/docs/ScReferralTableList.md +293 -0
- package/dist/docs/ScRole.md +226 -0
- package/dist/docs/ScRoleMobile.md +199 -0
- package/dist/docs/ScSelect.md +270 -0
- package/dist/docs/ScSelection.md +256 -0
- package/dist/docs/ScSelectionList.md +272 -0
- package/dist/docs/ScSelectionPill.md +240 -0
- package/dist/docs/ScSelectionPillGroup.md +302 -0
- package/dist/docs/ScSettingsNav.md +212 -0
- package/dist/docs/ScSettingsTabComp.md +260 -0
- package/dist/docs/ScSideBarLogoUnit.md +340 -0
- package/dist/docs/ScSidebar.md +243 -0
- package/dist/docs/ScSidebarIcons.md +232 -0
- package/dist/docs/ScSidebarMenu.md +283 -0
- package/dist/docs/ScSidebarProfile.md +231 -0
- package/dist/docs/ScSidebarSwitchMenu.md +258 -0
- package/dist/docs/ScSlider.md +194 -0
- package/dist/docs/ScStoreCard.md +252 -0
- package/dist/docs/ScStrLogo.md +253 -0
- package/dist/docs/ScStreamoidWordmark.md +302 -0
- package/dist/docs/ScSubAgent.md +226 -0
- package/dist/docs/ScTabComp.md +308 -0
- package/dist/docs/ScTabField.md +258 -0
- package/dist/docs/ScTabSwitcher.md +307 -0
- package/dist/docs/ScTableHeader.md +261 -0
- package/dist/docs/ScTableList.md +301 -0
- package/dist/docs/ScTableListMobile.md +282 -0
- package/dist/docs/ScTabs.md +268 -0
- package/dist/docs/ScTaxonomyPill.md +263 -0
- package/dist/docs/ScTextArea.md +259 -0
- package/dist/docs/ScTextField.md +324 -0
- package/dist/docs/ScThinkingStepIcon.md +249 -0
- package/dist/docs/ScTodoList.md +288 -0
- package/dist/docs/ScToggleSwitch.md +229 -0
- package/dist/docs/ScUsageHistoryMobile.md +194 -0
- package/dist/docs/ScVDivider.md +215 -0
- package/dist/docs/ScValueMappingL1.md +256 -0
- package/dist/docs/ScVersion.md +251 -0
- package/dist/docs/ScWorkspace.md +233 -0
- package/dist/docs/ScWorkspaceCard.md +234 -0
- package/dist/docs/ScWorkspaceSettingsMobile.md +265 -0
- package/dist/docs/ScWorkspaceSwitchCard.md +314 -0
- package/dist/docs/ScWorkspaceSwitchMobile.md +241 -0
- package/dist/docs/ScWorkspaceSwitchMobileV2.md +278 -0
- package/dist/docs/StreamoidSidebar.md +403 -0
- package/dist/docs/StreamoidWorkspaceSwitcher.md +307 -0
- package/dist/docs/UsageHistoryMobile.md +235 -0
- package/dist/docs/components.json +4849 -0
- package/dist/index.css +43 -37
- package/dist/index.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/package.json +3 -2
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ScBeacon
|
|
3
|
+
package: "@streamoid/ui"
|
|
4
|
+
category: overlays
|
|
5
|
+
status: stable
|
|
6
|
+
renders: span[aria-hidden="true"] wrapping an inline 20×20 SVG
|
|
7
|
+
tags: [beacon, dot, status-dot, indicator, halo, tone, success, warning, error, neutral]
|
|
8
|
+
related: [ScBadges, ScStoreCard, ScCatalogixStoreTableList, ScRole]
|
|
9
|
+
do_not_confuse_with: [ScBadges, ScProgressBar]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# ScBeacon
|
|
13
|
+
|
|
14
|
+
**A two-ring status dot.** A 20×20 `<span>` holding an inline SVG: a solid 4px core
|
|
15
|
+
plus a 10px halo of the same colour at 24% opacity, both `currentColor`, tinted by
|
|
16
|
+
one of four `tone`s. It is decoration — `aria-hidden` by default.
|
|
17
|
+
|
|
18
|
+
## TL;DR for agents
|
|
19
|
+
|
|
20
|
+
- **Reach for it when:** a row or card needs a colour-coded state marker that sits
|
|
21
|
+
beside a text label ("● Live", "● Needs attention").
|
|
22
|
+
- **Don't reach for it when:** the marker must carry the words itself
|
|
23
|
+
(→ `ScBadges`), it's a quantity (→ `ScProgressBar`), or you wanted an animated
|
|
24
|
+
"new!" nudge — **it does not animate** (see Gotcha 1).
|
|
25
|
+
- **Three things that will bite you:**
|
|
26
|
+
1. Despite the name, **there is no pulse and no animation anywhere** in
|
|
27
|
+
`ScBeacon.module.css`. It's a static dot with a halo.
|
|
28
|
+
2. `tone` defaults to `"neutral"` ⚠️ — a grey status dot that says nothing.
|
|
29
|
+
3. `aria-hidden="true"` is baked on. Screen readers get nothing, so it must
|
|
30
|
+
always sit next to real text.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 1. How to use it
|
|
35
|
+
|
|
36
|
+
### Import
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
import { ScBeacon } from "@streamoid/ui";
|
|
40
|
+
import "@streamoid/ui/dist/index.css"; // once, at your app root
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Minimal usage
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
<ScBeacon tone="success" />
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Practically always paired with a label:
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
<span style={{ display: "inline-flex", alignItems: "center", gap: 8 }}>
|
|
53
|
+
<ScBeacon tone="error" />
|
|
54
|
+
Needs attention
|
|
55
|
+
</span>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Props
|
|
59
|
+
|
|
60
|
+
| Prop | Type | Default | Notes |
|
|
61
|
+
|---|---|---|---|
|
|
62
|
+
| `tone` | `"success"` \| `"warning"` \| `"error"` \| `"neutral"` | `"neutral"` | ⚠️ Real default. Sets `color:` on the span; the SVG paints with `currentColor`. **There is no `info`/blue tone** (unlike `ScBadges`). |
|
|
63
|
+
| `className` | `string` | – | Appended only when truthy (this component gets the concatenation right). |
|
|
64
|
+
| `...props` | `HTMLAttributes<HTMLSpanElement>` | – | Spread onto the `<span>` **after** `aria-hidden`, so `style`, `title`, `aria-hidden={false}` + `aria-label`, `data-*` and `onClick` all work. |
|
|
65
|
+
|
|
66
|
+
`ScBeaconTone` is exported as a standalone type and reused by `ScStoreCard`
|
|
67
|
+
(`tone`) and `ScCatalogixStoreTableList` (`statusTone`).
|
|
68
|
+
|
|
69
|
+
### Tone → token
|
|
70
|
+
|
|
71
|
+
| `tone` | Colour |
|
|
72
|
+
|---|---|
|
|
73
|
+
| `success` | `--alias-text-and-icons-success` |
|
|
74
|
+
| `warning` | `--alias-text-and-icons-warning` |
|
|
75
|
+
| `error` | `--alias-text-and-icons-error` |
|
|
76
|
+
| `neutral` | `--alias-text-and-icons-muted` |
|
|
77
|
+
|
|
78
|
+
### Recipes
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
// Table status cell — dot + text, one accessible label
|
|
82
|
+
<td>
|
|
83
|
+
<span style={{ display: "inline-flex", alignItems: "center", gap: 8 }}>
|
|
84
|
+
<ScBeacon tone={STATUS_TONE[row.status]} />
|
|
85
|
+
<span>{STATUS_LABEL[row.status]}</span>
|
|
86
|
+
</span>
|
|
87
|
+
</td>
|
|
88
|
+
|
|
89
|
+
// Smaller dot — the SVG is width/height 100%, so resizing the span scales it
|
|
90
|
+
<ScBeacon tone="success" style={{ width: 12, height: 12 }} />
|
|
91
|
+
|
|
92
|
+
// Expose it to assistive tech when there is genuinely no adjacent text
|
|
93
|
+
<ScBeacon tone="error" aria-hidden={false} role="img" aria-label="Feed sync failed" />
|
|
94
|
+
|
|
95
|
+
// Share the tone type with your own status map
|
|
96
|
+
import type { ScBeaconTone } from "@streamoid/ui";
|
|
97
|
+
const STATUS_TONE: Record<FeedStatus, ScBeaconTone> = {
|
|
98
|
+
live: "success", syncing: "warning", failed: "error", draft: "neutral",
|
|
99
|
+
};
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## 2. Where to use it
|
|
105
|
+
|
|
106
|
+
- **Inside `ScStoreCard`** — the card's footer renders `<ScBeacon tone={tone} />`
|
|
107
|
+
beside the product count and creation date. That is its main job today.
|
|
108
|
+
- **Table status columns** — `ScCatalogixStoreTableList` takes a `statusTone` of the
|
|
109
|
+
same `ScBeaconTone` type, though it paints its own dot via a
|
|
110
|
+
`STATUS_TONE_CLASS` map rather than mounting this component.
|
|
111
|
+
- **Any list where a full `ScBadges` chip is too heavy** — dense tables where one
|
|
112
|
+
coloured dot plus the plain status word reads better than five coloured boxes.
|
|
113
|
+
|
|
114
|
+
No host app renders it directly; it reaches the screen through `ScStoreCard`.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 3. When to use it
|
|
119
|
+
|
|
120
|
+
### Use it when
|
|
121
|
+
|
|
122
|
+
- The state is **already named in adjacent text** and you only need the colour cue.
|
|
123
|
+
- The row is **dense** and a bordered/filled chip would be visual noise.
|
|
124
|
+
- You are building a component that should look like `ScStoreCard`'s footer.
|
|
125
|
+
|
|
126
|
+
### Don't use it — reach for this instead
|
|
127
|
+
|
|
128
|
+
| Situation | Use instead |
|
|
129
|
+
|---|---|
|
|
130
|
+
| The marker must carry the label itself ("Revoked", "Live") | `ScBadges` |
|
|
131
|
+
| Admin / Member role marker | `ScRole` / `ScRoleMobile` |
|
|
132
|
+
| An `info`/blue state | `ScBadges variant="info"` — this component has no blue tone |
|
|
133
|
+
| A numeric count on an icon (notification badge) | no DS component; build it app-local |
|
|
134
|
+
| Progress or a quantity | `ScProgressBar` |
|
|
135
|
+
| An animated onboarding "new here!" nudge | no DS component — `ScGuide` is the guided-tour callout; this dot does not animate |
|
|
136
|
+
| An interactive filter chip | `ScSelectionPill` |
|
|
137
|
+
|
|
138
|
+
### Don't confuse with
|
|
139
|
+
|
|
140
|
+
| You may actually want | Not this |
|
|
141
|
+
|---|---|
|
|
142
|
+
| `ScBadges` — text chip, 5 variants (incl. `info`), 2 skins, no spread | `ScBeacon` — dot only, 4 tones, full HTML spread |
|
|
143
|
+
| `ScProgressBar` — a meter | Not a status marker |
|
|
144
|
+
| An animated attention beacon | The name suggests it; the CSS contains no `@keyframes` |
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 4. Why to use it
|
|
149
|
+
|
|
150
|
+
- **The halo is the point.** A 24%-opacity ring around a solid core makes a small
|
|
151
|
+
dot legible against both dark and light surfaces without a border — a plain
|
|
152
|
+
`border-radius: 50%` div disappears on a low-contrast row.
|
|
153
|
+
- **`currentColor` throughout.** Both circles paint from `currentColor`, so the tone
|
|
154
|
+
is set once on the span and a `color` in your own `className` overrides the whole
|
|
155
|
+
mark. No fills to keep in sync.
|
|
156
|
+
- **Four semantic tokens, not four hex values.** The tones read the same
|
|
157
|
+
`--alias-text-and-icons-{success,warning,error,muted}` used by `ScBadges`,
|
|
158
|
+
`ScMenuOptions` and `ScMappingCard`'s status icons, so a "warning" is one colour
|
|
159
|
+
product-wide.
|
|
160
|
+
- **`ScBeaconTone` is the shared status vocabulary.** Exporting the type lets store
|
|
161
|
+
cards, store tables and your own status maps agree on four states instead of each
|
|
162
|
+
inventing five.
|
|
163
|
+
- **It cannot break a layout.** `inline-flex`, `flex-shrink: 0`, fixed 20×20 — safe
|
|
164
|
+
in a table cell or a truncating flex row.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Gotchas
|
|
169
|
+
|
|
170
|
+
**1. It does not pulse.** `ScBeacon.module.css` is 22 lines: a size, a
|
|
171
|
+
`flex-shrink`, and four colour classes. No `@keyframes`, no `animation`, no
|
|
172
|
+
`transition`. If you need the pulsing onboarding nudge the catalog describes, you
|
|
173
|
+
must add the animation yourself via `className`.
|
|
174
|
+
|
|
175
|
+
**2. `tone` defaults to `"neutral"`.** Forget it and you get a muted grey dot — a
|
|
176
|
+
status indicator communicating no status.
|
|
177
|
+
|
|
178
|
+
```tsx
|
|
179
|
+
// WRONG — grey dot regardless of state
|
|
180
|
+
<ScBeacon />
|
|
181
|
+
|
|
182
|
+
// RIGHT
|
|
183
|
+
<ScBeacon tone={row.failed ? "error" : "success"} />
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
**3. `aria-hidden="true"` is hardcoded.** By design — it's decoration. But it means
|
|
187
|
+
a dot-only status column is invisible to screen readers. Either keep a text label
|
|
188
|
+
beside it, or override: `aria-hidden={false} role="img" aria-label="…"` (the spread
|
|
189
|
+
comes after the attribute, so your override wins).
|
|
190
|
+
|
|
191
|
+
**4. No `size` prop.** Fixed `1.25rem` (20px). It *does* scale if you set both
|
|
192
|
+
`width` and `height` (the SVG is `100%`/`100%`) — but you must set both, via
|
|
193
|
+
`style` or `className`. Setting only one squashes it.
|
|
194
|
+
|
|
195
|
+
**5. A `color` you set on the span silently beats `tone`.** The tone class only sets
|
|
196
|
+
`color` on the span, and the SVG paints from `currentColor` — so
|
|
197
|
+
`style={{ color: … }}` (inline, always wins) or a same-specificity `.yourClass
|
|
198
|
+
{ color: … }` that happens to come later in the stylesheet overrides the tone
|
|
199
|
+
entirely. An *inherited* `color` from an ancestor does **not** override it: the tone
|
|
200
|
+
class targets the element directly and beats inheritance. Pick one mechanism.
|
|
201
|
+
|
|
202
|
+
**6. Four tones only — no `info`.** `ScBadges` has five variants; `ScBeacon` has
|
|
203
|
+
four. If your status map has a blue "info" state you must collapse it to `neutral`
|
|
204
|
+
or use a badge.
|
|
205
|
+
|
|
206
|
+
**7. It's a `<span>`, so it inherits `font-size`-driven line boxes.** In a text run
|
|
207
|
+
it sits on the baseline and can look vertically off; wrap it in an
|
|
208
|
+
`inline-flex; align-items: center` container with the label rather than relying on
|
|
209
|
+
inline layout.
|
|
210
|
+
|
|
211
|
+
**8. `onClick` works (via the spread) but there is no cursor, role or focus.** If
|
|
212
|
+
the dot is a control, wrap it in a real button — don't just add a handler to an
|
|
213
|
+
`aria-hidden` span.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## In the wild
|
|
218
|
+
|
|
219
|
+
_No host render site found — used by the agent runtime / composed internally._
|
|
220
|
+
|
|
221
|
+
Concretely: it is composed inside the DS, not called from the dashboards —
|
|
222
|
+
|
|
223
|
+
```tsx
|
|
224
|
+
// npm-components packages/ui/src/SC-StoreCard/ScStoreCard.tsx:33
|
|
225
|
+
<div className={styles.footer}>
|
|
226
|
+
<ScBeacon tone={tone} />
|
|
227
|
+
<span className={styles.products}>{noOfProducts}</span>
|
|
228
|
+
<span className={styles.date}>{dateCreated}</span>
|
|
229
|
+
</div>
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Where it most likely belongs next: the **Catalogix stores table**
|
|
233
|
+
(`ScCatalogixStoreTableList` already accepts a `statusTone: ScBeaconTone` but draws
|
|
234
|
+
its own dot through `STATUS_TONE_CLASS` — mounting `ScBeacon` there would remove the
|
|
235
|
+
duplicate), and any future feed/job status list in Catalogix or Photogenix.
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Related
|
|
240
|
+
|
|
241
|
+
- `ScBadges` — the text-carrying sibling; use it when the state must be named in the chip.
|
|
242
|
+
- `ScStoreCard` — the DS parent that renders a beacon today.
|
|
243
|
+
- `ScCatalogixStoreTableList` — reuses `ScBeaconTone` for its `statusTone` prop.
|
|
244
|
+
- `ScRole` — the dedicated Admin/Member marker.
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ScBillingHistoryHeader
|
|
3
|
+
package: "@streamoid/ui"
|
|
4
|
+
category: billing
|
|
5
|
+
status: stable
|
|
6
|
+
renders: div
|
|
7
|
+
tags: [invoice, billing-history, table-header, column-headers, invoices, header-row]
|
|
8
|
+
related: [ScBillingHistoryTableList, ScHeader, ScBillingLogsTableHeader, ScTableHeader]
|
|
9
|
+
do_not_confuse_with: [ScBillingLogsTableHeader, ScTableHeader, ScHeader, ScMobileTopNav]
|
|
10
|
+
used_by: [cxo]
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# ScBillingHistoryHeader
|
|
14
|
+
|
|
15
|
+
**The fixed column-header strip for the invoice-history table.** Four `ScHeader`
|
|
16
|
+
cells — `Invoice` · `Amount` · `Date` · `Action` — on a neutral fill, at the exact
|
|
17
|
+
widths its row sibling `ScBillingHistoryTableList` expects. It takes **no content
|
|
18
|
+
props at all**: the labels are baked in.
|
|
19
|
+
|
|
20
|
+
## TL;DR for agents
|
|
21
|
+
|
|
22
|
+
- **Reach for it when:** you are rendering the Billing → *Invoice history* table
|
|
23
|
+
and are about to stack `ScBillingHistoryTableList` rows under it.
|
|
24
|
+
- **Don't reach for it when:** your columns differ in any way (→ compose `ScHeader`
|
|
25
|
+
cells yourself), or you need the credit-logs header (→ `ScBillingLogsTableHeader`).
|
|
26
|
+
- **Three things that will bite you:**
|
|
27
|
+
1. `className` is the **only** prop. The four labels are hardcoded English and
|
|
28
|
+
cannot be renamed, reordered, removed or extended.
|
|
29
|
+
2. It is **not sticky**. Wrap it yourself if rows scroll under it.
|
|
30
|
+
3. `state="none"` is hardcoded on every cell, so there are **no sort arrows** even
|
|
31
|
+
though `ScHeader` supports `"up"`/`"down"`.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 1. How to use it
|
|
36
|
+
|
|
37
|
+
### Import
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
import { ScBillingHistoryHeader, ScBillingHistoryTableList } from "@streamoid/ui";
|
|
41
|
+
import "@streamoid/ui/dist/index.css"; // once, at your app root
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Minimal usage
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
<ScBillingHistoryHeader />
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Props
|
|
51
|
+
|
|
52
|
+
| Prop | Type | Default | Notes |
|
|
53
|
+
|---|---|---|---|
|
|
54
|
+
| `className` | `string` | – | Appended to the root class. **The only prop.** |
|
|
55
|
+
|
|
56
|
+
The interface does **not** extend `HTMLAttributes`, and although the component
|
|
57
|
+
destructures `...props`, it never spreads them — so `style`, `onClick`, `id` and
|
|
58
|
+
`data-*` are both a type error *and* a no-op.
|
|
59
|
+
|
|
60
|
+
### Fixed layout (must match your rows)
|
|
61
|
+
|
|
62
|
+
| Cell | Label | Width |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| 1 | `Invoice` | `flex: 1` (`align-items: flex-start`) |
|
|
65
|
+
| 2 | `Amount` | `7.5rem`, `flex-shrink: 0` |
|
|
66
|
+
| 3 | `Date` | `7.5rem`, `flex-shrink: 0` |
|
|
67
|
+
| 4 | `Action` | `10rem`, `flex-shrink: 0` |
|
|
68
|
+
|
|
69
|
+
Root: `padding: 0.75rem 1rem`, `gap: 1.5rem`, background
|
|
70
|
+
`--alias-fill-neutral-neutral`. `ScBillingHistoryTableList` uses the same gap and
|
|
71
|
+
horizontal padding (with `1.25rem` vertical), which is why the columns line up.
|
|
72
|
+
|
|
73
|
+
### Recipes
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
// Sticky header over a scrolling, rounded list (the real CXO/settings pattern)
|
|
77
|
+
<div
|
|
78
|
+
className="flex-1 min-h-0 w-full relative"
|
|
79
|
+
style={{
|
|
80
|
+
backgroundColor: "var(--alias-surface-base)",
|
|
81
|
+
borderRadius: "var(--radius-3xl)",
|
|
82
|
+
border: "1px solid var(--alias-border-subtle)",
|
|
83
|
+
overflowY: "auto",
|
|
84
|
+
}}
|
|
85
|
+
>
|
|
86
|
+
<div style={{ position: "sticky", top: 0, zIndex: 1, backgroundColor: "var(--alias-surface-base)" }}>
|
|
87
|
+
<ScBillingHistoryHeader />
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
{invoices.length === 0 && <EmptyRow>No invoices found</EmptyRow>}
|
|
91
|
+
|
|
92
|
+
{invoices.map((inv) => (
|
|
93
|
+
<ScBillingHistoryTableList key={inv._id} invoiceNumber={inv.number} amount={inv.total} date={inv.date} />
|
|
94
|
+
))}
|
|
95
|
+
</div>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
// Need different columns? Don't fork this component — compose ScHeader.
|
|
100
|
+
<div style={{ display: "flex", gap: "var(--spacing-6xl)", padding: "var(--spacing-xl) var(--spacing-3xl)",
|
|
101
|
+
background: "var(--alias-fill-neutral-neutral)" }}>
|
|
102
|
+
<ScHeader text="App" state="none" style={{ flex: 1 }} />
|
|
103
|
+
<ScHeader text="Credits" state="none" style={{ flexShrink: 0, width: "5rem", justifyContent: "flex-end" }} />
|
|
104
|
+
</div>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## 2. Where to use it
|
|
110
|
+
|
|
111
|
+
- **Billing → Invoice history tab**, immediately above the invoice rows. That is
|
|
112
|
+
its only surface.
|
|
113
|
+
- Rendered from the shared **`@streamoid/settings`** billing page
|
|
114
|
+
(`packages/settings/src/billing-content.tsx`, `InvoiceHistoryTable`), which
|
|
115
|
+
**CXO** mounts through `settings-content.tsx`. The sibling tab ("Usage logs") in
|
|
116
|
+
that same file **hand-rolls** its header with raw `ScHeader` cells rather than
|
|
117
|
+
using `ScBillingLogsTableHeader` — see that component's README for why.
|
|
118
|
+
- No mobile twin, and none is needed: the narrow-screen invoice list uses stacked
|
|
119
|
+
`InvoiceHistoryMobile` cards that carry their own labels. That path is currently
|
|
120
|
+
wired only in CXO's `src/app/components/mobile-billing-content.tsx:231`, reached
|
|
121
|
+
from CXO's orphaned local `billing-content.tsx`, so mobile billing is effectively
|
|
122
|
+
unwired today.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 3. When to use it
|
|
127
|
+
|
|
128
|
+
### Use it when
|
|
129
|
+
|
|
130
|
+
- Your columns are exactly `Invoice / Amount / Date / Action`, i.e. you are
|
|
131
|
+
rendering `ScBillingHistoryTableList` rows.
|
|
132
|
+
|
|
133
|
+
### Don't use it — reach for this instead
|
|
134
|
+
|
|
135
|
+
| Situation | Use instead |
|
|
136
|
+
|---|---|
|
|
137
|
+
| Credit/usage logs columns (`App`, `Activity`, `Used by`, `Date`, `Credits`) | `ScBillingLogsTableHeader` — but read its README first; production hand-rolls that one |
|
|
138
|
+
| Any other column set, or renamed/localised labels | `ScHeader` cells in your own flex row (see the second recipe) |
|
|
139
|
+
| A sortable header | `ScHeader` with `state="up"` / `"down"` and your own click handling |
|
|
140
|
+
| A page title bar, not a table header | `ScHeader` alone, or your page-header composition |
|
|
141
|
+
| Mobile invoice list | `InvoiceHistoryMobile` (self-labelling cards, no header row) |
|
|
142
|
+
|
|
143
|
+
### Don't confuse with
|
|
144
|
+
|
|
145
|
+
| You may actually want | Not this |
|
|
146
|
+
|---|---|
|
|
147
|
+
| `ScHeader` — the single header **cell** primitive (`text`, `state`, spreads props) | This is a fixed 4-cell **strip** |
|
|
148
|
+
| `ScBillingLogsTableHeader` — the 6-column credit-logs strip | Same shape, different labels and widths; the two are not interchangeable |
|
|
149
|
+
| `ScTableHeader` — the generic list/table header used elsewhere in the library | Not billing-specific and not width-matched to the invoice rows |
|
|
150
|
+
| `ScMobileTopNav` — a mobile screen's top bar | Nothing to do with tables |
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## 4. Why to use it
|
|
155
|
+
|
|
156
|
+
- **The widths are the contract.** The `7.5rem / 7.5rem / 10rem` trio and the
|
|
157
|
+
`1.5rem` gap are duplicated in `ScBillingHistoryTableList`; using both components
|
|
158
|
+
guarantees alignment, whereas a hand-rolled header drifts the moment either side
|
|
159
|
+
changes.
|
|
160
|
+
- **Header typography and fill come from tokens** via `ScHeader` and
|
|
161
|
+
`--alias-fill-neutral-neutral`, so the strip stays legible in light mode.
|
|
162
|
+
- **Zero API surface means zero drift** — every invoice table in the product shows
|
|
163
|
+
the same four labels in the same order.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Gotchas
|
|
168
|
+
|
|
169
|
+
**1. There is nothing to configure.** No labels, no widths, no sort state, no
|
|
170
|
+
`style`. If you need any of that, compose `ScHeader` yourself — copying this
|
|
171
|
+
component's flex values is a two-line job.
|
|
172
|
+
|
|
173
|
+
**2. Not sticky, not rounded.** It renders a plain flex row with a square neutral
|
|
174
|
+
background. The rounded card, the sticky positioning and the top-corner clipping all
|
|
175
|
+
belong to your wrapper.
|
|
176
|
+
|
|
177
|
+
**3. Extra props are silently dropped.** `({ className, ...props })` destructures a
|
|
178
|
+
rest object that is never used. TypeScript will reject the prop, but if you spread
|
|
179
|
+
a wider object through `as any`, it disappears with no warning.
|
|
180
|
+
|
|
181
|
+
**4. Missing `className` leaks `"undefined"` into the class list** (root is
|
|
182
|
+
`styles.scBillingHistoryHeader + " " + className`).
|
|
183
|
+
|
|
184
|
+
**5. No table semantics.** No `<table>`, `<thead>`, `<th>` or `role="columnheader"`
|
|
185
|
+
— screen readers see four unrelated strings, and nothing associates them with the
|
|
186
|
+
row cells beneath.
|
|
187
|
+
|
|
188
|
+
**6. Alignment is by convention, not by grid.** If you render a custom row under
|
|
189
|
+
this header, you must repeat `gap: 1.5rem`, `padding: … 1rem` and the three fixed
|
|
190
|
+
widths, or the columns will not line up.
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## In the wild
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
// npm-components packages/settings/src/billing-content.tsx:1117
|
|
198
|
+
<div style={{ position: "sticky", top: 0, zIndex: 1, backgroundColor: "var(--alias-surface-base)" }}>
|
|
199
|
+
<ScBillingHistoryHeader />
|
|
200
|
+
</div>
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Related
|
|
206
|
+
|
|
207
|
+
- `ScBillingHistoryTableList` — the row component this header exists for.
|
|
208
|
+
- `ScHeader` — the cell primitive; use it directly for any other column set.
|
|
209
|
+
- `ScBillingLogsTableHeader` — the credit-logs sibling strip.
|
|
210
|
+
- `InvoiceHistoryMobile` — the stacked mobile alternative to this whole table.
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ScBillingHistoryTableList
|
|
3
|
+
package: "@streamoid/ui"
|
|
4
|
+
category: billing
|
|
5
|
+
status: stable
|
|
6
|
+
renders: div
|
|
7
|
+
tags: [invoice, invoice-row, billing-history, table-row, download-invoice, receipt, billing]
|
|
8
|
+
related: [ScBillingHistoryHeader, InvoiceHistoryMobile, ScPairtext, ScBillingLogsTableList, ScTableList]
|
|
9
|
+
do_not_confuse_with: [ScBillingLogsTableList, ScTableList, InvoiceHistoryMobile]
|
|
10
|
+
used_by: [cxo]
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# ScBillingHistoryTableList
|
|
14
|
+
|
|
15
|
+
**One invoice row in the billing-history table.** Invoice number (flexible width,
|
|
16
|
+
ellipsised), amount in success green, date, and a "Download invoice" `ScPairtext`
|
|
17
|
+
with a download icon — width-matched to `ScBillingHistoryHeader` and separated by a
|
|
18
|
+
bottom hairline.
|
|
19
|
+
|
|
20
|
+
## TL;DR for agents
|
|
21
|
+
|
|
22
|
+
- **Reach for it when:** you are listing invoices under a `ScBillingHistoryHeader`.
|
|
23
|
+
- **Don't reach for it when:** the row is a credit/usage log entry
|
|
24
|
+
(→ `ScBillingLogsTableList`), a generic data row (→ `ScTableList`), or the mobile
|
|
25
|
+
stacked invoice card (→ `InvoiceHistoryMobile`).
|
|
26
|
+
- **Four things that will bite you:**
|
|
27
|
+
1. `onRowClick` is wired to the root's `onClick` **before** `...props` is spread,
|
|
28
|
+
so passing your own `onClick` **replaces** `onRowClick` entirely.
|
|
29
|
+
2. The download cell has **no handler of its own**. Either treat the whole row as
|
|
30
|
+
the download target, or hit-test the event against the CSS-module class (what
|
|
31
|
+
production does).
|
|
32
|
+
3. `amount` is **always success green**. Refunds, failures and zero amounts all
|
|
33
|
+
render as if they were paid.
|
|
34
|
+
4. Every value has demo-data default (`"# INV-2800-2026"`, `"₹320.89"`,
|
|
35
|
+
`"Nov 9th, 2025"`), so a wrong prop name shows plausible fake data instead of
|
|
36
|
+
failing.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 1. How to use it
|
|
41
|
+
|
|
42
|
+
### Import
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import { ScBillingHistoryHeader, ScBillingHistoryTableList } from "@streamoid/ui";
|
|
46
|
+
import "@streamoid/ui/dist/index.css"; // once, at your app root
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Minimal usage
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
<ScBillingHistoryTableList
|
|
53
|
+
invoiceNumber="# INV-3F92A0C1"
|
|
54
|
+
amount="₹4,999.00"
|
|
55
|
+
date="Nov 9th, 2025"
|
|
56
|
+
onRowClick={() => downloadInvoice(inv)}
|
|
57
|
+
/>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Props
|
|
61
|
+
|
|
62
|
+
| Prop | Type | Default | Notes |
|
|
63
|
+
|---|---|---|---|
|
|
64
|
+
| `invoiceNumber` | `string` | `"# INV-2800-2026"` | ⚠️ Real default. `flex: 1`, ellipsised. You supply the `#` prefix. |
|
|
65
|
+
| `amount` | `string` | `"₹320.89"` | ⚠️ Real default. Fixed `7.5rem`. Colour is hardcoded success green. |
|
|
66
|
+
| `date` | `string` | `"Nov 9th, 2025"` | ⚠️ Real default. Fixed `7.5rem`. Pre-formatted by you. |
|
|
67
|
+
| `downloadLabel` | `string` | `"Download invoice"` | ⚠️ Real default. Label of the icon+text action cell (`10rem`). |
|
|
68
|
+
| `onRowClick` | `() => void` | – | Bound to the root `onClick`. **Overridden by an `onClick` you pass** — see Gotcha 1. |
|
|
69
|
+
| `className` | `string` | – | Appended to the root class. |
|
|
70
|
+
| `...props` | `HTMLAttributes<HTMLDivElement>` | – | Spread onto the root **after** `onClick={onRowClick}`. |
|
|
71
|
+
|
|
72
|
+
### Layout (matches `ScBillingHistoryHeader`)
|
|
73
|
+
|
|
74
|
+
| Cell | Width | Styling |
|
|
75
|
+
|---|---|---|
|
|
76
|
+
| `invoiceNumber` | `flex: 1` | primary text, 14px, ellipsis |
|
|
77
|
+
| `amount` | `7.5rem` | **`--alias-text-and-icons-success`**, 16px semibold, ellipsis |
|
|
78
|
+
| `date` | `7.5rem` | primary text, 14px, ellipsis |
|
|
79
|
+
| download `ScPairtext` | `10rem` | `cursor: pointer`, download icon + `downloadLabel` |
|
|
80
|
+
|
|
81
|
+
Root: `padding: 1.25rem 1rem`, `gap: 1.5rem`, bottom border
|
|
82
|
+
`--alias-border-subtle`. No background of its own — it inherits the wrapper's.
|
|
83
|
+
|
|
84
|
+
### Recipes
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
// Whole row is the download target (simplest correct wiring)
|
|
88
|
+
{invoices.map((inv) => (
|
|
89
|
+
<ScBillingHistoryTableList
|
|
90
|
+
key={inv._id}
|
|
91
|
+
invoiceNumber={`# ${inv.transactionId.slice(0, 16).toUpperCase()}`}
|
|
92
|
+
amount={`${currencySymbol}${(inv.amount / 100).toFixed(2)}`}
|
|
93
|
+
date={formatDate(inv.createdAt)}
|
|
94
|
+
downloadLabel="Download invoice"
|
|
95
|
+
onRowClick={() => handleDownload(inv)}
|
|
96
|
+
/>
|
|
97
|
+
))}
|
|
98
|
+
|
|
99
|
+
// Only the "Download invoice" cell should act (the production pattern):
|
|
100
|
+
// use onClick + hit-test the pairtext by its emitted CSS-module class.
|
|
101
|
+
const handleRowClick = (e: React.MouseEvent<HTMLDivElement>, inv: Invoice) => {
|
|
102
|
+
const hit = (e.target as HTMLElement).closest(
|
|
103
|
+
`.${CSS.escape("ScBillingHistoryTableList_scPairtextInstance")}`,
|
|
104
|
+
);
|
|
105
|
+
if (hit) handleDownload(inv);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
<ScBillingHistoryTableList
|
|
109
|
+
invoiceNumber={n}
|
|
110
|
+
amount={a}
|
|
111
|
+
date={d}
|
|
112
|
+
onClick={(e: React.MouseEvent<HTMLDivElement>) => handleRowClick(e, inv)}
|
|
113
|
+
/>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 2. Where to use it
|
|
119
|
+
|
|
120
|
+
- **Billing → Invoice history tab**, stacked directly under
|
|
121
|
+
`ScBillingHistoryHeader` inside a rounded, scrolling surface. Its only surface.
|
|
122
|
+
- Rendered from the shared **`@streamoid/settings`** billing page
|
|
123
|
+
(`packages/settings/src/billing-content.tsx`, `InvoiceHistoryTable`), which
|
|
124
|
+
**CXO** mounts through `settings-content.tsx`.
|
|
125
|
+
- The mobile equivalent is `InvoiceHistoryMobile` (stacked card, self-labelling) —
|
|
126
|
+
currently only wired from CXO's orphaned local mobile billing file.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## 3. When to use it
|
|
131
|
+
|
|
132
|
+
### Use it when
|
|
133
|
+
|
|
134
|
+
- The row is an **invoice** with exactly the `Invoice / Amount / Date / Download`
|
|
135
|
+
shape, and you are pairing it with `ScBillingHistoryHeader`.
|
|
136
|
+
- The amount is a **successful charge** (the green is not negotiable).
|
|
137
|
+
|
|
138
|
+
### Don't use it — reach for this instead
|
|
139
|
+
|
|
140
|
+
| Situation | Use instead |
|
|
141
|
+
|---|---|
|
|
142
|
+
| Credit/usage log entry (App, Activity, Used by, Date, Credits) | `ScBillingLogsTableList` — but read its README; production hand-rolls that table |
|
|
143
|
+
| A generic data row with your own columns | `ScTableList` (or raw divs mirroring the header's widths) |
|
|
144
|
+
| Narrow-screen invoice list | `InvoiceHistoryMobile` |
|
|
145
|
+
| A refunded / failed / pending amount that must not read as green | Hand-roll the row — this component cannot express a non-success amount |
|
|
146
|
+
| A real download link (right-click → Save as, middle-click, keyboard) | Your own `<a href download>` — this row is a `div` with no link semantics |
|
|
147
|
+
|
|
148
|
+
### Don't confuse with
|
|
149
|
+
|
|
150
|
+
| You may actually want | Not this |
|
|
151
|
+
|---|---|
|
|
152
|
+
| `ScBillingLogsTableList` — 6 columns (`app`, `activity`, `usedBy`, `date`, `creditsUsed`, `balance`), credits hardcoded **red** | This one is 4 columns with the amount hardcoded **green** |
|
|
153
|
+
| `InvoiceHistoryMobile` — exported, props `invoiceId` / `date` / `amount` | Not a responsive mode of this row |
|
|
154
|
+
| `ScInvoiceHistoryMobile` (folder `SC-InvoiceHistory-Mobile`) | ⚠️ **Not exported from the package index** — importing it from `@streamoid/ui` fails. Use `InvoiceHistoryMobile`. |
|
|
155
|
+
| `ScTableList` — the generic list row used across the dashboards | Not width-matched to the invoice header |
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## 4. Why to use it
|
|
160
|
+
|
|
161
|
+
- **Column widths are the contract.** `flex:1 / 7.5rem / 7.5rem / 10rem` plus a
|
|
162
|
+
`1.5rem` gap and `1rem` side padding mirror `ScBillingHistoryHeader` exactly, so
|
|
163
|
+
the header and the rows can't drift apart.
|
|
164
|
+
- **Every cell already truncates.** All three text cells are
|
|
165
|
+
`nowrap + overflow:hidden + ellipsis`, so a long transaction id degrades instead
|
|
166
|
+
of breaking the row.
|
|
167
|
+
- **The download affordance is a composed `ScPairtext`**, so the icon size, gap and
|
|
168
|
+
label typography match every other icon+text pair in the product.
|
|
169
|
+
- **Amount emphasis is tokenised** (`--alias-text-and-icons-success`), so it holds
|
|
170
|
+
contrast in light mode where a hardcoded `#00b96b` would not.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Gotchas
|
|
175
|
+
|
|
176
|
+
**1. `onClick` beats `onRowClick`.** The JSX is
|
|
177
|
+
`onClick={onRowClick} {...props}`, so a spread `onClick` overwrites it. Never pass
|
|
178
|
+
both.
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
// WRONG — onRowClick never fires
|
|
182
|
+
<ScBillingHistoryTableList onRowClick={download} onClick={logClick} />
|
|
183
|
+
|
|
184
|
+
// RIGHT — pick one; use onClick when you need the event object
|
|
185
|
+
<ScBillingHistoryTableList onClick={(e) => { logClick(); download(); }} />
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**2. The download cell has no handler.** `ScPairtext` only gets `cursor: pointer`.
|
|
189
|
+
Clicking it bubbles to the row. To make *only* that cell act, hit-test
|
|
190
|
+
`event.target.closest(".ScBillingHistoryTableList_scPairtextInstance")` — that
|
|
191
|
+
class name is part of the emitted CSS-module output and is what the live billing
|
|
192
|
+
page relies on. It is effectively a private API: if the module class prefix ever
|
|
193
|
+
changes, your hit-test silently stops matching.
|
|
194
|
+
|
|
195
|
+
**3. Green amounts, always.** `.amount` is `--alias-text-and-icons-success`. There
|
|
196
|
+
is no `variant`/`status` prop, so credits notes and failed charges look paid.
|
|
197
|
+
|
|
198
|
+
**4. Demo defaults hide typos.** Misspell `invoiceNumber` and the row cheerfully
|
|
199
|
+
shows `"# INV-2800-2026"` / `"₹320.89"` / `"Nov 9th, 2025"`. Check prop names
|
|
200
|
+
against the table when a row looks oddly plausible.
|
|
201
|
+
|
|
202
|
+
**5. Not a link, not focusable.** It is a `div` with an `onClick` — no `role`, no
|
|
203
|
+
`tabIndex`, no keyboard activation, no browser download semantics. Keyboard users
|
|
204
|
+
cannot download an invoice from this row.
|
|
205
|
+
|
|
206
|
+
**6. The bottom border is unconditional.** The last row keeps its hairline; if your
|
|
207
|
+
container has its own bottom edge you get a double line. Trim it from the wrapper.
|
|
208
|
+
|
|
209
|
+
**7. No background.** The row is transparent, so the wrapper owns the surface
|
|
210
|
+
colour — important when you also make the header sticky (the header needs an opaque
|
|
211
|
+
background or rows show through).
|
|
212
|
+
|
|
213
|
+
**8. Missing `className` leaks `"undefined"` into the class list.**
|
|
214
|
+
|
|
215
|
+
**9. Trailing spaces in the DOM.** `invoiceNumber`, `amount` and `date` each render
|
|
216
|
+
as `{value}` plus a literal space; trim before exact-match text assertions.
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## In the wild
|
|
221
|
+
|
|
222
|
+
```tsx
|
|
223
|
+
// npm-components packages/settings/src/billing-content.tsx:1138
|
|
224
|
+
<ScBillingHistoryTableList
|
|
225
|
+
key={inv._id}
|
|
226
|
+
invoiceNumber={inv.transactionId ? `# ${inv.transactionId.slice(0, 16).toUpperCase()}` : "# INV-UNKNOWN"}
|
|
227
|
+
amount={inv.amountPaid != null
|
|
228
|
+
? `${currencySymbol}${inv.amountPaid.toFixed(2)}`
|
|
229
|
+
: `${currencySymbol}${(inv.amount / 100).toFixed(2)}`}
|
|
230
|
+
date={formatDate(inv.createdAt)}
|
|
231
|
+
downloadLabel="Download invoice"
|
|
232
|
+
onClick={(e: React.MouseEvent<HTMLDivElement>) => handleRowClick(e, inv)}
|
|
233
|
+
/>
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## Related
|
|
239
|
+
|
|
240
|
+
- `ScBillingHistoryHeader` — the header strip whose widths this row mirrors.
|
|
241
|
+
- `InvoiceHistoryMobile` — the stacked mobile card for the same data.
|
|
242
|
+
- `ScBillingLogsTableList` — the credit-logs row sibling.
|
|
243
|
+
- `ScPairtext` — the icon+label pair used for the download cell.
|