@streamoid/ui 0.6.17 → 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 +36 -36
- package/dist/index.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/package.json +3 -2
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ScInfoPopup
|
|
3
|
+
package: "@streamoid/ui"
|
|
4
|
+
category: overlays
|
|
5
|
+
status: stable
|
|
6
|
+
renders: div (inline-block wrapper) > span[role="button"] trigger + absolutely-positioned div bubble (no portal)
|
|
7
|
+
tags: [info, popover, tooltip, help, hint, question-mark, popup, contextual]
|
|
8
|
+
related: [ScTextField, ScModal, ScGuide, ScBadges]
|
|
9
|
+
do_not_confuse_with: [ScModal, ScPopUpMenu, ScProfilePopup, ScGuide]
|
|
10
|
+
used_by: [catalogix]
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# ScInfoPopup
|
|
14
|
+
|
|
15
|
+
**The little ⓘ that opens a help bubble.** A self-contained 18px info glyph that,
|
|
16
|
+
on click, reveals a small absolutely-positioned card (max 300 × 100px, scrollable)
|
|
17
|
+
with a ✕ in the corner. It owns its own open state and its own outside-click
|
|
18
|
+
dismissal — you pass only `content`.
|
|
19
|
+
|
|
20
|
+
## TL;DR for agents
|
|
21
|
+
|
|
22
|
+
- **Reach for it when:** a label or a table header needs a short explanation, and
|
|
23
|
+
you want the standard ⓘ affordance next to it.
|
|
24
|
+
- **Don't reach for it when:** you're labelling a form field that is already a
|
|
25
|
+
`ScTextField` (it has an `info` prop that renders this for you), you need a
|
|
26
|
+
hover tooltip, you need a menu (→ `ScMenuOptions`), or the content is long
|
|
27
|
+
enough to need a dialog (→ `ScModal`).
|
|
28
|
+
- **Four things that will bite you:**
|
|
29
|
+
1. **It is not portalled.** The bubble is `position: absolute` with
|
|
30
|
+
`z-index: 2`, so any ancestor with `overflow: hidden` clips it — that
|
|
31
|
+
includes `ScDrawer`, `ScMappingCard` and most cards.
|
|
32
|
+
2. **No positioning logic.** Hardcoded `top: 18px; right: 0; translateX(50%)` —
|
|
33
|
+
it always hangs half-way off to the right and is cut by the viewport edge.
|
|
34
|
+
3. **Keyboard users can focus the trigger but cannot open it.** `tabIndex={0}` +
|
|
35
|
+
`role="button"` with an `onClick` only — no `onKeyDown`.
|
|
36
|
+
4. **You cannot control it.** No `open` / `onOpenChange` / `defaultOpen`, no
|
|
37
|
+
`trigger` slot, no `icon` prop. The glyph is a hardcoded inline SVG.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 1. How to use it
|
|
42
|
+
|
|
43
|
+
### Import
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
import { ScInfoPopup } from "@streamoid/ui";
|
|
47
|
+
import "@streamoid/ui/dist/index.css"; // once, at your app root
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Minimal usage
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
<ScInfoPopup content="Only rows that passed validation are exported." />
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Props
|
|
57
|
+
|
|
58
|
+
| Prop | Type | Default | Notes |
|
|
59
|
+
|---|---|---|---|
|
|
60
|
+
| `content` | `ReactNode` | – | The bubble body. Rendered inside a `<span>`, so block-level children are technically invalid HTML — keep it to text / inline nodes. |
|
|
61
|
+
| `className` | `string` | – | On the **outer wrapper** (the `inline-block` that contains the trigger *and* the bubble), appended after `.scInfoPopup`. There is no way to class the bubble itself. |
|
|
62
|
+
|
|
63
|
+
That's the whole API. No `...props` spread — anything else is a type error.
|
|
64
|
+
|
|
65
|
+
### What it actually renders
|
|
66
|
+
|
|
67
|
+
```html
|
|
68
|
+
<div class="scInfoPopup {className}"> <!-- position:relative; display:inline-block -->
|
|
69
|
+
<span class="infoIcon" role="button" tabindex="0" aria-label="More info">
|
|
70
|
+
<svg width="18" height="18">…</svg> <!-- hardcoded ⓘ, --alias-text-and-icons-tertiary -->
|
|
71
|
+
</span>
|
|
72
|
+
<!-- only when open: -->
|
|
73
|
+
<div class="content"> <!-- position:absolute; top:18px; right:0;
|
|
74
|
+
translateX(50%); max-width:300px;
|
|
75
|
+
z-index:2; radius 6px; padding 8px;
|
|
76
|
+
background --alias-surface-base -->
|
|
77
|
+
<div class="closeIconContainer"><SiconClose size={18} /></div>
|
|
78
|
+
<span class="contentText">{content}</span> <!-- 12px; max-height:100px; overflow-y:auto -->
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
⚠️ The root is a **`<div>`**, not a span — dropping one inside a `<p>` or another
|
|
84
|
+
inline-only element is invalid nesting even though the wrapper is `inline-block`.
|
|
85
|
+
|
|
86
|
+
### Recipes
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
// On a form field — DON'T nest your own; ScTextField already composes one
|
|
90
|
+
<ScTextField label="Feed URL" required info="We poll this URL every 6 hours." />
|
|
91
|
+
|
|
92
|
+
// Beside a table column header
|
|
93
|
+
<div style={{ display: "flex", alignItems: "center" }}>
|
|
94
|
+
<span>Match rate</span>
|
|
95
|
+
<ScInfoPopup content="Share of feed rows that matched an existing product." />
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
// Rich (inline) content
|
|
99
|
+
<ScInfoPopup content={<>Supported: <code>CSV</code>, <code>TSV</code>, <code>XLSX</code>.</>} />
|
|
100
|
+
|
|
101
|
+
// Neutralise the baked-in 12px side margins when it must sit tight to a label
|
|
102
|
+
<ScInfoPopup className="tight" content="…" />
|
|
103
|
+
// .tight > span:first-child { margin: 0; } /* .infoIcon has margin: 0 12px */
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## 2. Where to use it
|
|
109
|
+
|
|
110
|
+
- **Form field labels** — but via `ScTextField`'s `info` prop, which renders this
|
|
111
|
+
component at `ScTextField.tsx:106`. That is by far the highest-traffic path.
|
|
112
|
+
- **Table / list column headers** where the metric needs a definition.
|
|
113
|
+
- **Settings rows** explaining what a toggle actually does.
|
|
114
|
+
- **Catalogix** wraps it as `app/components/InfoPopup`, a pass-through that
|
|
115
|
+
preserves the old `content`-only API.
|
|
116
|
+
|
|
117
|
+
Catalogix is the only direct host consumer. CXO/Photogenix/Artifax get it
|
|
118
|
+
indirectly through `ScTextField`.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## 3. When to use it
|
|
123
|
+
|
|
124
|
+
### Use it when
|
|
125
|
+
|
|
126
|
+
- The explanation is **one or two short sentences** and genuinely optional.
|
|
127
|
+
- The trigger belongs **inline next to a label**, not as a separate control.
|
|
128
|
+
- You want the standard glyph, bubble surface and dismissal without wiring state.
|
|
129
|
+
|
|
130
|
+
### Don't use it — reach for this instead
|
|
131
|
+
|
|
132
|
+
| Situation | Use instead |
|
|
133
|
+
|---|---|
|
|
134
|
+
| Explaining a `ScTextField` label | `ScTextField`'s own `info` prop (it renders this for you) |
|
|
135
|
+
| A hover tooltip on an icon button | plain `title` attribute, or your app's tooltip — this is click-to-open |
|
|
136
|
+
| Content longer than ~2 sentences, or with actions/links to click | `ScModal` |
|
|
137
|
+
| A step-by-step onboarding callout with Skip / Next | `ScGuide` |
|
|
138
|
+
| A menu of actions | `ScMenuOptions` rows in your own positioned container |
|
|
139
|
+
| A status/validation message under a field | `ScTextField`'s helper/error text |
|
|
140
|
+
| A dismissible warning banner | `CreditWarningBanner` (exported without the `Sc` prefix), or your app's banner |
|
|
141
|
+
| A popover you need to open/close programmatically | roll your own — this component does not expose its state |
|
|
142
|
+
|
|
143
|
+
### Don't confuse with
|
|
144
|
+
|
|
145
|
+
| You may actually want | Not this |
|
|
146
|
+
|---|---|
|
|
147
|
+
| `ScModal` — portalled, scrimmed, blocking, caller-controlled | `ScInfoPopup` is inline, unportalled, self-controlled |
|
|
148
|
+
| `ScPopUpMenu` / `ScMenuOptions` — a *menu row*, no popover at all | `ScInfoPopup` is the only component here that actually pops something up |
|
|
149
|
+
| `ScProfilePopup` — a large profile *panel body* you position yourself | `ScInfoPopup` positions itself (badly) and owns its own state |
|
|
150
|
+
| `ScGuide` — heading + body + Skip/Next, for guided tours | `ScInfoPopup` has no actions |
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## 4. Why to use it
|
|
155
|
+
|
|
156
|
+
- **Zero wiring.** Open state, the outside-click listener (added/removed with
|
|
157
|
+
`open`) and the ✕ are all internal. There is no app-level `OutsideClick`
|
|
158
|
+
dependency, so it works identically in all four hosts.
|
|
159
|
+
- **The glyph is theme-aware and consistent.** The trigger circle, stroke and dot
|
|
160
|
+
are drawn with `--alias-text-and-icons-tertiary`, so the ⓘ has the same weight
|
|
161
|
+
and colour as every other tertiary icon in the product — the usual hand-rolled
|
|
162
|
+
version is a hardcoded grey that disappears in light mode.
|
|
163
|
+
- **The bubble is already token-correct** — `--alias-surface-base` on a soft shadow
|
|
164
|
+
with `--alias-text-and-icons-primary` copy at 12px.
|
|
165
|
+
- **Overflow is bounded.** `max-width: 300px` + `max-height: 100px; overflow-y: auto`
|
|
166
|
+
means an over-long help string degrades into a small scroller instead of blowing
|
|
167
|
+
out your layout.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Gotchas
|
|
172
|
+
|
|
173
|
+
**1. It is not portalled — clipping is the number-one failure.** The bubble lives
|
|
174
|
+
inside the trigger's `position: relative` wrapper. Any ancestor with
|
|
175
|
+
`overflow: hidden` cuts it off: `ScDrawer` (twice over), `ScMappingCard`,
|
|
176
|
+
`ScProgressBar`, most cards, and any `text-overflow: ellipsis` row.
|
|
177
|
+
|
|
178
|
+
```tsx
|
|
179
|
+
// WRONG — bubble is clipped by the drawer's overflow:hidden
|
|
180
|
+
<ScDrawer><ScInfoPopup content="…" /></ScDrawer>
|
|
181
|
+
|
|
182
|
+
// RIGHT — put the ⓘ outside the clipping container, or use ScModal for the explanation
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**2. `z-index: 2` only.** Anything with a higher z-index inside the same stacking
|
|
186
|
+
context paints over the bubble. There is no prop to raise it; you'd have to
|
|
187
|
+
override `.content` from an ancestor selector.
|
|
188
|
+
|
|
189
|
+
**3. Placement is hardcoded and has no collision handling.**
|
|
190
|
+
`top: 18px; right: 0; transform: translateX(50%)` means the bubble is *centred on
|
|
191
|
+
the trigger's right edge* — half of it hangs to the right of the icon. Placed near
|
|
192
|
+
the right edge of the screen (or of a narrow card), it is cut off. There is no
|
|
193
|
+
`side` / `align` / flip prop.
|
|
194
|
+
|
|
195
|
+
**4. Keyboard cannot open it.** The trigger is a `<span role="button" tabIndex={0}>`
|
|
196
|
+
with `onClick` and **no `onKeyDown`** — Tab focuses it, Enter and Space do nothing.
|
|
197
|
+
The ✕ is a bare `<div onClick>` with no role or tabIndex at all. If your surface
|
|
198
|
+
must be keyboard-accessible, don't hide required information here.
|
|
199
|
+
|
|
200
|
+
**5. `aria-label="More info"` is hardcoded English,** as is nothing else — but there
|
|
201
|
+
is no label prop, so it cannot be localised without changing the DS.
|
|
202
|
+
|
|
203
|
+
**6. The trigger carries `margin: 0 12px`.** You get 12px of horizontal space on
|
|
204
|
+
both sides whether you want it or not; a `gap` on your flex parent stacks on top of
|
|
205
|
+
it. Neutralise via `className` if you need it flush.
|
|
206
|
+
|
|
207
|
+
**7. It is click-to-open, not hover.** Users expecting a tooltip will not discover
|
|
208
|
+
it. Also: opening `stopPropagation()`s the click, so a click on the ⓘ never reaches
|
|
209
|
+
a clickable parent row — usually what you want, occasionally surprising.
|
|
210
|
+
|
|
211
|
+
**8. Dismissal is on `mousedown`, and re-clicking the ⓘ does not toggle.** The
|
|
212
|
+
trigger only ever calls `setOpen(true)`; the outside-click listener closes it. A
|
|
213
|
+
second click on the icon lands *inside* the wrapper, so it stays open. Close via
|
|
214
|
+
the ✕ or by clicking elsewhere.
|
|
215
|
+
|
|
216
|
+
**9. `className` styles the wrapper, not the bubble.** There is no
|
|
217
|
+
`contentClassName`. To restyle the bubble you must write a descendant selector from
|
|
218
|
+
your own class.
|
|
219
|
+
|
|
220
|
+
**10. `content` renders inside a `<span>`.** Passing a `<div>`/`<p>`/list produces
|
|
221
|
+
invalid nesting (it usually still renders, but don't rely on it). Keep it inline.
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## In the wild
|
|
226
|
+
|
|
227
|
+
```jsx
|
|
228
|
+
// catalogix/dashboard app/components/InfoPopup/index.jsx:9
|
|
229
|
+
export default function InfoPopup({ content }) {
|
|
230
|
+
return <ScInfoPopup content={content} />;
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
And composed inside the DS itself:
|
|
235
|
+
|
|
236
|
+
```tsx
|
|
237
|
+
// npm-components packages/ui/src/SC-textField/ScTextField.tsx:106
|
|
238
|
+
{info != null && <ScInfoPopup content={info} />}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## Related
|
|
244
|
+
|
|
245
|
+
- `ScTextField` — pass `info` and it renders this popup beside the label; the preferred path.
|
|
246
|
+
- `ScModal` — for explanations too long or too interactive for a bubble.
|
|
247
|
+
- `ScGuide` — guided-tour callout with Skip / step / Next.
|
|
248
|
+
- `ScMenuOptions` — if what you actually wanted was a menu, not help text.
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ScIntialProfileCover
|
|
3
|
+
package: "@streamoid/ui"
|
|
4
|
+
category: profile
|
|
5
|
+
status: stable
|
|
6
|
+
renders: div
|
|
7
|
+
tags: [initials, avatar, workspace-tile, placeholder, cover, misspelled-prop, intial]
|
|
8
|
+
related: [ScDp, ScWorkspaceCard, ScProfile, ScWorkspaceSwitchCard]
|
|
9
|
+
do_not_confuse_with: [ScDp, ScProfileImageUpdate, ScProfile, ScBadges]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# ScIntialProfileCover
|
|
13
|
+
|
|
14
|
+
**An initials tile — 60px rounded square, no image path at all.** One grey box
|
|
15
|
+
(`fill-neutral-neutralselected`, `radius-3xl`) with centred initials. Note the
|
|
16
|
+
misspellings: the component is `ScIntialProfileCover` and its prop is **`intial`**.
|
|
17
|
+
Both are load-bearing — you must reproduce them to compile.
|
|
18
|
+
|
|
19
|
+
## TL;DR for agents
|
|
20
|
+
|
|
21
|
+
- **Reach for it when:** you need the workspace/profile *initials block* from the
|
|
22
|
+
Figma workspace rows and you know you will never have an image.
|
|
23
|
+
- **Don't reach for it when:** an image might exist (→ `ScDp`, which has an
|
|
24
|
+
`onError` → initials fallback), or you need any interactivity or `aria` (this
|
|
25
|
+
component forwards **nothing**).
|
|
26
|
+
- **Four things that will bite you:**
|
|
27
|
+
1. The prop is **`intial`** (sic), not `initial`. `initial="AC"` is a type error;
|
|
28
|
+
the tile silently shows the `"WS"` default if you cast around it.
|
|
29
|
+
2. It has **no image support**. Not `imageUrl`, not `children`.
|
|
30
|
+
3. It accepts **only** `intial`, `size`, `className`. No `style`, no `onClick`, no
|
|
31
|
+
`data-*`, no `aria-*` — `IScIntialProfileCoverProps` does not extend
|
|
32
|
+
`HTMLAttributes`.
|
|
33
|
+
4. The initials text is a fixed `1rem`; `size` does not scale it, and the box
|
|
34
|
+
clips (`overflow: hidden` on `.initials`).
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 1. How to use it
|
|
39
|
+
|
|
40
|
+
### Import
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
import { ScIntialProfileCover } from "@streamoid/ui";
|
|
44
|
+
import "@streamoid/ui/dist/index.css"; // once, at your app root
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Minimal usage
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
<ScIntialProfileCover intial="AC" />
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Props
|
|
54
|
+
|
|
55
|
+
| Prop | Type | Default | Notes |
|
|
56
|
+
|---|---|---|---|
|
|
57
|
+
| `intial` | `string` | `"WS"` | ⚠️ **Misspelled prop name** and ⚠️ a real default. Rendered with a literal trailing space (`{intial} `). |
|
|
58
|
+
| `size` | `number` | – | px. When set, applies `width`/`height` **and zeroes the 1rem padding**. Unset → the CSS default of `3.75rem` (60px) **with** 1rem padding. |
|
|
59
|
+
| `className` | `string` | – | Appended (concatenated unconditionally — a literal `undefined` class when omitted). |
|
|
60
|
+
|
|
61
|
+
That is the entire API — three props, and no spread.
|
|
62
|
+
|
|
63
|
+
### What `size` changes
|
|
64
|
+
|
|
65
|
+
| `size` | Box | Padding | Notes |
|
|
66
|
+
|---|---|---|---|
|
|
67
|
+
| unset | `3.75rem` × `3.75rem` from CSS | `var(--spacing-3xl)` = 1rem | the Figma default block |
|
|
68
|
+
| set | `{size}px` inline | `0` | inline `style` on the root; the CSS class rule is overridden |
|
|
69
|
+
|
|
70
|
+
### Recipes
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
// Workspace row tile, initials derived from the name
|
|
74
|
+
<ScIntialProfileCover
|
|
75
|
+
intial={ws.initials || ws.name.split(/\s+/).slice(0, 2).map((w) => w[0]?.toUpperCase() ?? "").join("") || "WS"}
|
|
76
|
+
className="shrink-0"
|
|
77
|
+
/>
|
|
78
|
+
|
|
79
|
+
// Smaller tile — remember one character only, the font does not scale
|
|
80
|
+
<ScIntialProfileCover intial="A" size={32} />
|
|
81
|
+
|
|
82
|
+
// Needs a click, an image fallback, or an aria label? use ScDp instead
|
|
83
|
+
<button type="button" onClick={open} aria-label={`Open ${ws.name}`}>
|
|
84
|
+
<ScDp type={ws.imageUrl ? "image" : "initial"} variant="workspace" initial="AC" imageUrl={ws.imageUrl} size={60} />
|
|
85
|
+
</button>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 2. Where to use it
|
|
91
|
+
|
|
92
|
+
Composed inside two DS components, not usually placed directly:
|
|
93
|
+
|
|
94
|
+
| Parent | Role |
|
|
95
|
+
|---|---|
|
|
96
|
+
| `ScWorkspaceCard` | the workspace tile at the head of the card |
|
|
97
|
+
| `StreamoidWorkspaceSwitcher` | the tile on each workspace row in the switch modal |
|
|
98
|
+
|
|
99
|
+
Everything newer reaches for `ScDp` instead — `ScWorkspaceSwitchCard`,
|
|
100
|
+
`ScWorkspaceSwitchMobileV2` and the sidebar shells all use `ScDp variant="workspace"`.
|
|
101
|
+
Treat `ScIntialProfileCover` as the older, image-less spelling of the same idea.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 3. When to use it
|
|
106
|
+
|
|
107
|
+
### Use it when
|
|
108
|
+
|
|
109
|
+
- You are inside `ScWorkspaceCard` / `StreamoidWorkspaceSwitcher` territory and want
|
|
110
|
+
to match those tiles exactly (60px, `radius-3xl`).
|
|
111
|
+
- The entity provably has no avatar (a brand-new workspace, a placeholder row).
|
|
112
|
+
|
|
113
|
+
### Don't use it — reach for this instead
|
|
114
|
+
|
|
115
|
+
| Situation | Use instead |
|
|
116
|
+
|---|---|
|
|
117
|
+
| An avatar that may have an image | `ScDp` (`type="image"` + `imageUrl`, with initials fallback) |
|
|
118
|
+
| A person avatar (circle) | `ScDp` with `variant="profile"` |
|
|
119
|
+
| Clickable / focusable / labelled tile | `ScDp` wrapped in a `<button>` — this component forwards nothing |
|
|
120
|
+
| Avatar upload / delete | `ScProfileImageUpdate` |
|
|
121
|
+
| Avatar + name + subtext row | `ScProfile` |
|
|
122
|
+
| A whole workspace row | `ScWorkspaceSwitchCard` |
|
|
123
|
+
| A small text chip / count | `ScBadges` |
|
|
124
|
+
|
|
125
|
+
### Don't confuse with
|
|
126
|
+
|
|
127
|
+
| You may actually want | Not this |
|
|
128
|
+
|---|---|
|
|
129
|
+
| `ScDp` — `initial` (spelled correctly), `variant`, `imageUrl`, `size` default 60, error fallback, accepts `style` | `ScIntialProfileCover` is initials-only with the misspelled `intial` |
|
|
130
|
+
| `ScProfileImageUpdate` — editable avatar | this is display-only |
|
|
131
|
+
| `ScProfile` — identity **row** | this is one tile |
|
|
132
|
+
|
|
133
|
+
`ScDp` vs `ScIntialProfileCover` is the mis-pick to watch: they look identical at
|
|
134
|
+
60px in the workspace variant. **If an image could ever exist, it is `ScDp`.**
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 4. Why to use it
|
|
139
|
+
|
|
140
|
+
- **It is the exact Figma block** used by the workspace card and switch modal:
|
|
141
|
+
60 × 60, `radius-3xl` (1rem), `fill-neutral-neutralselected`, `text-md/medium`
|
|
142
|
+
centred. Re-deriving those four values by hand is how tile sizes drift between the
|
|
143
|
+
card and the modal.
|
|
144
|
+
- **Token-driven**, so the grey and the text flip with the theme.
|
|
145
|
+
- **Genuinely tiny** — if you know there is no image, it is a smaller commitment than
|
|
146
|
+
`ScDp`'s image/error state machine.
|
|
147
|
+
|
|
148
|
+
Honestly though: `ScDp` gives you all of this plus the image path, accepts `style`,
|
|
149
|
+
and is the one the newer components use. Prefer it in new code unless you are
|
|
150
|
+
matching an existing `ScIntialProfileCover` tile.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Gotchas
|
|
155
|
+
|
|
156
|
+
**1. The prop is `intial`, not `initial`.**
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
// WRONG — type error (and if you cast past it, you get "WS")
|
|
160
|
+
<ScIntialProfileCover initial="AC" />
|
|
161
|
+
|
|
162
|
+
// RIGHT
|
|
163
|
+
<ScIntialProfileCover intial="AC" />
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The component name is misspelled too (`ScIntialProfileCover`). Both are the published
|
|
167
|
+
API — fixing them is a breaking change, so don't "correct" them locally.
|
|
168
|
+
|
|
169
|
+
**2. `intial` defaults to `"WS"`.** Forget it and every tile reads "WS".
|
|
170
|
+
|
|
171
|
+
**3. No `style` prop, no spread.** `IScIntialProfileCoverProps` is exactly
|
|
172
|
+
`{ intial?, size?, className? }`. `onClick`, `title`, `aria-label`, `data-testid` and
|
|
173
|
+
`style` are all rejected by TypeScript. Style it through `className`, and wrap it for
|
|
174
|
+
interaction.
|
|
175
|
+
|
|
176
|
+
```tsx
|
|
177
|
+
// WRONG — type error; also no keyboard access even if it compiled
|
|
178
|
+
<ScIntialProfileCover intial="AC" onClick={open} />
|
|
179
|
+
|
|
180
|
+
// RIGHT
|
|
181
|
+
<button type="button" onClick={open} aria-label="Acme workspace">
|
|
182
|
+
<ScIntialProfileCover intial="AC" />
|
|
183
|
+
</button>
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
**4. `size` also zeroes the padding.** The two configurations are not just different
|
|
187
|
+
sizes: unset gives `60px + 1rem padding`, set gives `{size}px + 0 padding`. The
|
|
188
|
+
initials therefore sit closer to the edge at custom sizes.
|
|
189
|
+
|
|
190
|
+
**5. Long initials clip, and the font never scales.** `.initials` is
|
|
191
|
+
`font-size-md` (1rem) with `overflow: hidden` + `text-overflow: ellipsis`. Three
|
|
192
|
+
letters, or two letters at `size={24}`, get cut.
|
|
193
|
+
|
|
194
|
+
**6. It is a rounded square, never a circle.** `radius-3xl` (1rem) is baked in. For a
|
|
195
|
+
person avatar you want `ScDp variant="profile"`, or override `border-radius` via
|
|
196
|
+
`className`.
|
|
197
|
+
|
|
198
|
+
**7. `className` is concatenated unconditionally** → `class="scIntialProfileCover undefined"`
|
|
199
|
+
when omitted.
|
|
200
|
+
|
|
201
|
+
**8. Trailing space in the text node** (`{intial} `) — `trim()` before asserting in
|
|
202
|
+
tests.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## In the wild
|
|
207
|
+
|
|
208
|
+
_No host render site found — used by the agent runtime / composed internally._
|
|
209
|
+
|
|
210
|
+
Composed at `src/SC-workspaceCard/ScWorkspaceCard.tsx:35` and
|
|
211
|
+
`src/SC-WorkspaceSwitcher/streamoid-workspace-switcher.tsx:101`:
|
|
212
|
+
|
|
213
|
+
```tsx
|
|
214
|
+
// packages/ui src/SC-WorkspaceSwitcher/streamoid-workspace-switcher.tsx:101
|
|
215
|
+
<ScIntialProfileCover
|
|
216
|
+
intial={workspace.initials || deriveInitials(workspace.name)}
|
|
217
|
+
className="shrink-0"
|
|
218
|
+
/>
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Host apps that need a workspace tile use `ScDp` instead (e.g. artifax
|
|
222
|
+
`packages/shared/src/components/DashboardSidebar.tsx:698`). If a host ever needs this
|
|
223
|
+
tile directly it belongs on a workspace-creation or empty-state screen, where no
|
|
224
|
+
avatar can exist yet.
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Related
|
|
229
|
+
|
|
230
|
+
- `ScDp` — the avatar with an image path and an error fallback; prefer it in new code.
|
|
231
|
+
- `ScWorkspaceCard` — renders this tile plus name/role/owner and a CTA.
|
|
232
|
+
- `StreamoidWorkspaceSwitcher` — the switch modal whose rows use this tile.
|
|
233
|
+
- `ScProfileImageUpdate` — the editable avatar.
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ScInvoiceHistoryMobile
|
|
3
|
+
exported: false
|
|
4
|
+
package: "@streamoid/ui"
|
|
5
|
+
category: mobile
|
|
6
|
+
status: deprecated
|
|
7
|
+
renders: div
|
|
8
|
+
tags: [mobile, billing, invoice, history, row, unexported, dead-code, superseded]
|
|
9
|
+
related: [InvoiceHistoryMobile, ScUsageHistoryMobile, ScBillingHistoryTableList]
|
|
10
|
+
do_not_confuse_with: [InvoiceHistoryMobile, ScUsageHistoryMobile, UsageHistoryMobile]
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# ScInvoiceHistoryMobile
|
|
14
|
+
|
|
15
|
+
**Do not use — this component is not shipped.** It is an earlier copy of the mobile
|
|
16
|
+
invoice row that never made it into the public surface: it is absent from
|
|
17
|
+
`src/index.ts`, so its JS is not in `dist/index.js`, its class names are not in
|
|
18
|
+
`dist/index.css`, and `IScInvoiceHistoryMobileProps` is not in `dist/index.d.ts`.
|
|
19
|
+
The shipped component is **`InvoiceHistoryMobile`** (no `Sc` prefix) in
|
|
20
|
+
`src/SC-InvoiceHistoryMobile/`.
|
|
21
|
+
|
|
22
|
+
## TL;DR for agents
|
|
23
|
+
|
|
24
|
+
- **Reach for it when:** never. Use `InvoiceHistoryMobile` instead.
|
|
25
|
+
- **Don't reach for it when:** always — importing `ScInvoiceHistoryMobile` from
|
|
26
|
+
`@streamoid/ui` throws/undefined at runtime and fails to type-check.
|
|
27
|
+
- **Two things that will bite you:**
|
|
28
|
+
1. **It is not exported.** Neither the component nor its CSS reaches `dist/`.
|
|
29
|
+
Verified: `grep -c scInvoiceHistoryMobile dist/index.css` → `0`.
|
|
30
|
+
2. **The prop names differ from the shipped twin.** Here the id prop is
|
|
31
|
+
`invoiceNumber`; in `InvoiceHistoryMobile` it is `invoiceId`.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 1. How to use it
|
|
36
|
+
|
|
37
|
+
You don't. Import the shipped sibling:
|
|
38
|
+
|
|
39
|
+
### Import
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
// The component that actually ships:
|
|
43
|
+
import { InvoiceHistoryMobile } from "@streamoid/ui";
|
|
44
|
+
import "@streamoid/ui/dist/index.css"; // once, at your app root
|
|
45
|
+
|
|
46
|
+
// NOT THIS — `ScInvoiceHistoryMobile` is not an export of @streamoid/ui
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Minimal usage
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
<InvoiceHistoryMobile
|
|
53
|
+
invoiceId={`# ${inv.number}`}
|
|
54
|
+
date={formatDate(inv.createdAt)}
|
|
55
|
+
amount={`$${inv.amountPaid.toFixed(2)}`}
|
|
56
|
+
/>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Props
|
|
60
|
+
|
|
61
|
+
Documented only so you can recognise the difference when migrating a call site.
|
|
62
|
+
|
|
63
|
+
| Prop | Type | Default | Notes |
|
|
64
|
+
|---|---|---|---|
|
|
65
|
+
| `invoiceNumber` | `string` | `"# INV-2800-2026"` | ⚠️ Demo default. **Renamed to `invoiceId`** in the shipped twin. |
|
|
66
|
+
| `date` | `string` | `"Nov 9th, 2025"` | ⚠️ Demo default. Same name in the shipped twin. |
|
|
67
|
+
| `amount` | `string` | `"₹320.89"` | ⚠️ Demo default in rupees. Same name in the shipped twin. Always success-green. |
|
|
68
|
+
| `onClick` | `(e: React.MouseEvent) => void` | – | **Only exists here.** The shipped twin has no click handler. |
|
|
69
|
+
| `className` | `string` | – | Unguarded concat — renders a literal `undefined` class when omitted. |
|
|
70
|
+
| `...props` | `React.HTMLAttributes<HTMLDivElement>` | – | **Only exists here.** The shipped twin accepts no extra props. |
|
|
71
|
+
|
|
72
|
+
### Mapping between the two
|
|
73
|
+
|
|
74
|
+
| This (unexported) | Shipped `InvoiceHistoryMobile` |
|
|
75
|
+
|---|---|
|
|
76
|
+
| `invoiceNumber` | `invoiceId` |
|
|
77
|
+
| `date` | `date` |
|
|
78
|
+
| `amount` | `amount` |
|
|
79
|
+
| `onClick` | *(none — wrap the row in your own clickable `div`)* |
|
|
80
|
+
| `...props` spread | *(none — `className` only)* |
|
|
81
|
+
| `className` (unguarded) | `className` (guarded with `?? ""`) |
|
|
82
|
+
|
|
83
|
+
Styling is otherwise equivalent: 16px padding, 12px gap, 0.5px bottom divider, a
|
|
84
|
+
truncating text column (`flex: 1 0 0` here, `flex: 1` in the shipped twin) and a
|
|
85
|
+
`flex-shrink: 0` green amount.
|
|
86
|
+
|
|
87
|
+
### Recipes
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
// Migrating an old call site
|
|
91
|
+
// BEFORE (would not have compiled against the published package anyway)
|
|
92
|
+
// <ScInvoiceHistoryMobile invoiceNumber={id} date={d} amount={a} onClick={open} />
|
|
93
|
+
|
|
94
|
+
// AFTER
|
|
95
|
+
<div onClick={open} className="cursor-pointer">
|
|
96
|
+
<InvoiceHistoryMobile invoiceId={id} date={d} amount={a} />
|
|
97
|
+
</div>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 2. Where to use it
|
|
103
|
+
|
|
104
|
+
Nowhere. The surface it was drawn for — the "Invoice history" tab of CXO's mobile
|
|
105
|
+
billing screen — is served by `InvoiceHistoryMobile`
|
|
106
|
+
(`cxo-dashboard/src/app/components/mobile-billing-content.tsx:231`).
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 3. When to use it
|
|
111
|
+
|
|
112
|
+
### Use it when
|
|
113
|
+
|
|
114
|
+
Never. Treat this folder as source to delete, or as the place to land the fix if the
|
|
115
|
+
team decides the shipped row should regain `onClick` and a props spread (which are the
|
|
116
|
+
two capabilities this copy has and the shipped one lacks).
|
|
117
|
+
|
|
118
|
+
### Don't use it — reach for this instead
|
|
119
|
+
|
|
120
|
+
| Situation | Use instead |
|
|
121
|
+
|---|---|
|
|
122
|
+
| A mobile invoice row | `InvoiceHistoryMobile` (`src/SC-InvoiceHistoryMobile/`) |
|
|
123
|
+
| A mobile credit-usage row | `UsageHistoryMobile` (`src/SC-UsageHistoryMobile/`) |
|
|
124
|
+
| Desktop invoice table | `ScBillingHistoryHeader` + `ScBillingHistoryTableList` |
|
|
125
|
+
| A clickable mobile row with DS-provided semantics | `ScTableListMobile` (`onRowClick`) |
|
|
126
|
+
|
|
127
|
+
### Don't confuse with
|
|
128
|
+
|
|
129
|
+
| You may actually want | Not this |
|
|
130
|
+
|---|---|
|
|
131
|
+
| `InvoiceHistoryMobile` — the exported row, prop `invoiceId`, no `onClick` | This one is unexported and uses `invoiceNumber` |
|
|
132
|
+
| `ScUsageHistoryMobile` — the *other* unexported duplicate, for usage rows | Same situation, different data |
|
|
133
|
+
| `UsageHistoryMobile` — the exported usage row | Different data (red credits, four fields) |
|
|
134
|
+
|
|
135
|
+
The library contains **two** of these unexported `Sc*`-prefixed duplicates —
|
|
136
|
+
`ScInvoiceHistoryMobile` and `ScUsageHistoryMobile` — while the exported pair
|
|
137
|
+
(`InvoiceHistoryMobile`, `UsageHistoryMobile`) are the only two components in the
|
|
138
|
+
**mobile** family without the `Sc` prefix. (Other un-prefixed exports exist in the
|
|
139
|
+
package — `CreditWarningBanner`, `StreamoidSidebar` — but none are mobile.) That
|
|
140
|
+
inversion is the single easiest naming mistake to make in this family.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## 4. Why to use it
|
|
145
|
+
|
|
146
|
+
There is no reason to. What the shipped twin gives you that this does not: it is
|
|
147
|
+
actually in the bundle. What this gives you that the shipped twin does not: `onClick`
|
|
148
|
+
and a props spread — capabilities you can reproduce today with a wrapper `div`.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Gotchas
|
|
153
|
+
|
|
154
|
+
**1. It is not importable.** `src/index.ts` exports
|
|
155
|
+
`./SC-InvoiceHistoryMobile/InvoiceHistoryMobile` and nothing from
|
|
156
|
+
`./SC-InvoiceHistory-Mobile/`. The two folder names differ only by a hyphen.
|
|
157
|
+
|
|
158
|
+
**2. Its CSS is not in the shipped stylesheet.** Because nothing imports the module,
|
|
159
|
+
esbuild never sees `ScInvoiceHistoryMobile.module.css`. Even if you deep-imported the
|
|
160
|
+
`.tsx` from source, in a consumer repo the row would render **unstyled**.
|
|
161
|
+
|
|
162
|
+
**3. `invoiceNumber` vs `invoiceId`.** Copying an old snippet across produces an
|
|
163
|
+
undefined-prop that silently falls back to the demo default `"# INV-2800-2026"`.
|
|
164
|
+
|
|
165
|
+
**4. `className` is concatenated unguarded** here (`+ " " + className`), so it emits a
|
|
166
|
+
literal `undefined` class — one of the small differences from the shipped twin, which
|
|
167
|
+
guards with `?? ""`.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## In the wild
|
|
172
|
+
|
|
173
|
+
_No host render site found — used by the agent runtime / composed internally._
|
|
174
|
+
|
|
175
|
+
To be precise: it cannot have one — it is not exported, so no host *could* render it.
|
|
176
|
+
It is not agent-runtime either; it is dead source. The surface it was drawn for uses
|
|
177
|
+
the exported twin at
|
|
178
|
+
`cxo-dashboard/src/app/components/mobile-billing-content.tsx:231`.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Related
|
|
183
|
+
|
|
184
|
+
- `InvoiceHistoryMobile` — **use this**; the shipped mobile invoice row.
|
|
185
|
+
- `UsageHistoryMobile` — the shipped mobile usage row.
|
|
186
|
+
- `ScUsageHistoryMobile` — the other unexported duplicate, same story.
|
|
187
|
+
- `ScBillingHistoryHeader` / `ScBillingHistoryTableList` — the desktop table.
|