@buoy-gg/jotai 7.0.30 → 7.0.31
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/lib/commonjs/index.js +7 -0
- package/lib/commonjs/jotai/components/JotaiAtomBrowser.js +30 -119
- package/lib/commonjs/jotai/components/JotaiAtomChangeItem.js +19 -7
- package/lib/commonjs/jotai/components/JotaiAtomDetailContent.js +41 -5
- package/lib/commonjs/jotai/components/JotaiAtomValueDetailContent.js +444 -0
- package/lib/commonjs/jotai/components/JotaiEventFilterView.js +15 -0
- package/lib/commonjs/jotai/components/JotaiModal.js +158 -23
- package/lib/commonjs/jotai/utils/jotaiStateStore.js +9 -4
- package/lib/commonjs/jotai/utils/testIDs.js +90 -0
- package/lib/commonjs/jotai/utils/watchAtoms.js +8 -2
- package/lib/commonjs/jotai/utils/writeSemantics.js +47 -0
- package/lib/module/index.js +1 -0
- package/lib/module/jotai/components/JotaiAtomBrowser.js +33 -122
- package/lib/module/jotai/components/JotaiAtomChangeItem.js +19 -7
- package/lib/module/jotai/components/JotaiAtomDetailContent.js +41 -5
- package/lib/module/jotai/components/JotaiAtomValueDetailContent.js +442 -0
- package/lib/module/jotai/components/JotaiEventFilterView.js +15 -0
- package/lib/module/jotai/components/JotaiModal.js +158 -23
- package/lib/module/jotai/utils/jotaiStateStore.js +9 -4
- package/lib/module/jotai/utils/testIDs.js +82 -0
- package/lib/module/jotai/utils/watchAtoms.js +8 -2
- package/lib/module/jotai/utils/writeSemantics.js +43 -0
- package/lib/typescript/index.d.ts +1 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/jotai/components/JotaiAtomBrowser.d.ts +6 -5
- package/lib/typescript/jotai/components/JotaiAtomBrowser.d.ts.map +1 -1
- package/lib/typescript/jotai/components/JotaiAtomChangeItem.d.ts +5 -0
- package/lib/typescript/jotai/components/JotaiAtomChangeItem.d.ts.map +1 -1
- package/lib/typescript/jotai/components/JotaiAtomDetailContent.d.ts.map +1 -1
- package/lib/typescript/jotai/components/JotaiAtomValueDetailContent.d.ts +40 -0
- package/lib/typescript/jotai/components/JotaiAtomValueDetailContent.d.ts.map +1 -0
- package/lib/typescript/jotai/components/JotaiEventFilterView.d.ts.map +1 -1
- package/lib/typescript/jotai/components/JotaiModal.d.ts.map +1 -1
- package/lib/typescript/jotai/sync/jotaiSyncAdapter.d.ts +1 -0
- package/lib/typescript/jotai/sync/jotaiSyncAdapter.d.ts.map +1 -1
- package/lib/typescript/jotai/types/index.d.ts +12 -0
- package/lib/typescript/jotai/types/index.d.ts.map +1 -1
- package/lib/typescript/jotai/utils/jotaiStateStore.d.ts +1 -1
- package/lib/typescript/jotai/utils/jotaiStateStore.d.ts.map +1 -1
- package/lib/typescript/jotai/utils/testIDs.d.ts +62 -0
- package/lib/typescript/jotai/utils/testIDs.d.ts.map +1 -0
- package/lib/typescript/jotai/utils/watchAtoms.d.ts +8 -2
- package/lib/typescript/jotai/utils/watchAtoms.d.ts.map +1 -1
- package/lib/typescript/jotai/utils/writeSemantics.d.ts +25 -0
- package/lib/typescript/jotai/utils/writeSemantics.d.ts.map +1 -0
- package/package.json +3 -3
|
@@ -16,21 +16,36 @@ import { useJotaiAtomChanges } from "../hooks/useJotaiAtomChanges";
|
|
|
16
16
|
import { JotaiAtomChangeItem } from "./JotaiAtomChangeItem";
|
|
17
17
|
import { JotaiAtomDetailContent, JotaiAtomDetailFooter } from "./JotaiAtomDetailContent";
|
|
18
18
|
import { JotaiAtomBrowser } from "./JotaiAtomBrowser";
|
|
19
|
+
import { JotaiAtomValueDetailContent } from "./JotaiAtomValueDetailContent";
|
|
19
20
|
import { JotaiEventFilterView } from "./JotaiEventFilterView";
|
|
21
|
+
import { JOTAI_TEST_ID, buildChangeRowTestIDs } from "../utils/testIDs";
|
|
20
22
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
23
|
+
/**
|
|
24
|
+
* Cell clipping, off in e2e bundles only.
|
|
25
|
+
*
|
|
26
|
+
* `removeClippedSubviews` unmounts the views of rows scrolled out of the
|
|
27
|
+
* window, which is what makes a long change list cheap — and which makes
|
|
28
|
+
* Detox report a perfectly rendered row as un-hittable. The env var is
|
|
29
|
+
* inlined at bundle time, so a bundler that does NOT inline it leaves the
|
|
30
|
+
* production behaviour exactly as it was.
|
|
31
|
+
*/
|
|
32
|
+
const CLIP_OFFSCREEN_CELLS = !(typeof process !== "undefined" && process.env?.EXPO_PUBLIC_E2E);
|
|
21
33
|
function EventsEmptyState({
|
|
22
34
|
isEnabled
|
|
23
35
|
}) {
|
|
24
36
|
return /*#__PURE__*/_jsxs(View, {
|
|
25
37
|
style: styles.emptyState,
|
|
38
|
+
testID: `${JOTAI_TEST_ID}-empty`,
|
|
26
39
|
children: [/*#__PURE__*/_jsx(Box, {
|
|
27
40
|
size: 32,
|
|
28
41
|
color: night.color.textTertiary
|
|
29
42
|
}), /*#__PURE__*/_jsx(Text, {
|
|
30
43
|
style: styles.emptyTitle,
|
|
44
|
+
testID: `${JOTAI_TEST_ID}-empty-title`,
|
|
31
45
|
children: "No atom changes"
|
|
32
46
|
}), /*#__PURE__*/_jsx(Text, {
|
|
33
47
|
style: styles.emptyText,
|
|
48
|
+
testID: `${JOTAI_TEST_ID}-empty-text`,
|
|
34
49
|
children: isEnabled ? "Atom changes will appear here as values update." : "Enable capture to start recording atom changes"
|
|
35
50
|
})]
|
|
36
51
|
});
|
|
@@ -53,6 +68,9 @@ export function JotaiModal({
|
|
|
53
68
|
const [showProModal, setShowProModal] = useState(false);
|
|
54
69
|
const [activeTab, setActiveTab] = useState(initialTab ?? "atoms");
|
|
55
70
|
const [selectedHistoryAtom, setSelectedHistoryAtom] = useState(null);
|
|
71
|
+
// The atom whose DETAIL SCREEN (live editor) is open — tapping a card in
|
|
72
|
+
// the atoms tab navigates here, mirroring ZustandModal's selectedStoreName.
|
|
73
|
+
const [selectedAtomLabel, setSelectedAtomLabel] = useState(null);
|
|
56
74
|
const [showFilters, setShowFilters] = useState(false);
|
|
57
75
|
const [ignoredPatterns, setIgnoredPatterns] = useState(new Set());
|
|
58
76
|
const {
|
|
@@ -137,6 +155,16 @@ export function JotaiModal({
|
|
|
137
155
|
}));
|
|
138
156
|
}
|
|
139
157
|
};
|
|
158
|
+
|
|
159
|
+
/** Drop the query AND put the header back. The X, and only the X. */
|
|
160
|
+
const closeSearch = useCallback(() => {
|
|
161
|
+
setSearchText("");
|
|
162
|
+
setFilter(prev => ({
|
|
163
|
+
...prev,
|
|
164
|
+
searchText: ""
|
|
165
|
+
}));
|
|
166
|
+
setIsSearchActive(false);
|
|
167
|
+
}, [setFilter]);
|
|
140
168
|
const handleChangePress = useCallback(change => {
|
|
141
169
|
setSelectedChangeId(change.id);
|
|
142
170
|
}, []);
|
|
@@ -158,6 +186,16 @@ export function JotaiModal({
|
|
|
158
186
|
setSelectedHistoryAtom(null);
|
|
159
187
|
setSelectedChangeId(null);
|
|
160
188
|
}, []);
|
|
189
|
+
const handleOpenAtom = useCallback(label => {
|
|
190
|
+
setSelectedAtomLabel(label);
|
|
191
|
+
// The desktop mirror hydrates an atom's full value on demand — this is
|
|
192
|
+
// the moment it becomes needed (was fired on card-expand before the
|
|
193
|
+
// detail screen replaced expansion).
|
|
194
|
+
onAtomExpand?.(label);
|
|
195
|
+
}, [onAtomExpand]);
|
|
196
|
+
const handleBackFromAtom = useCallback(() => {
|
|
197
|
+
setSelectedAtomLabel(null);
|
|
198
|
+
}, []);
|
|
161
199
|
const getAtomsSnapshot = useCallback(() => {
|
|
162
200
|
return displayedAtoms.reduce((acc, atom) => {
|
|
163
201
|
try {
|
|
@@ -216,12 +254,19 @@ export function JotaiModal({
|
|
|
216
254
|
setIsSearchActive(false);
|
|
217
255
|
}, [searchText, setFilter]);
|
|
218
256
|
const keyExtractor = useCallback(item => item.id, []);
|
|
257
|
+
|
|
258
|
+
// Built for the whole list rather than per row: the ordinal a row carries is
|
|
259
|
+
// a fact about the list around it. See ../utils/testIDs.ts for why an atom
|
|
260
|
+
// label alone cannot address a change row — and why the ordinal is counted
|
|
261
|
+
// per atom when one write can move several.
|
|
262
|
+
const rowTestIDs = useMemo(() => buildChangeRowTestIDs(activeChanges), [activeChanges]);
|
|
219
263
|
const renderItem = useCallback(({
|
|
220
264
|
item
|
|
221
265
|
}) => /*#__PURE__*/_jsx(JotaiAtomChangeItem, {
|
|
222
266
|
change: item,
|
|
223
|
-
onPress: handleChangePress
|
|
224
|
-
|
|
267
|
+
onPress: handleChangePress,
|
|
268
|
+
testID: rowTestIDs.get(item.id)
|
|
269
|
+
}), [handleChangePress, rowTestIDs]);
|
|
225
270
|
|
|
226
271
|
// ---- Header ----
|
|
227
272
|
|
|
@@ -233,7 +278,8 @@ export function JotaiModal({
|
|
|
233
278
|
onBack: () => setShowFilters(false)
|
|
234
279
|
}), /*#__PURE__*/_jsx(ModalHeader.Content, {
|
|
235
280
|
title: "Filters",
|
|
236
|
-
centered: true
|
|
281
|
+
centered: true,
|
|
282
|
+
testID: `${JOTAI_TEST_ID}-filters-title`
|
|
237
283
|
})]
|
|
238
284
|
});
|
|
239
285
|
}
|
|
@@ -243,7 +289,8 @@ export function JotaiModal({
|
|
|
243
289
|
onBack: handleBack
|
|
244
290
|
}), /*#__PURE__*/_jsx(ModalHeader.Content, {
|
|
245
291
|
title: `${selectedChange.atomLabel}/${selectedChange.category}`,
|
|
246
|
-
centered: true
|
|
292
|
+
centered: true,
|
|
293
|
+
testID: `${JOTAI_TEST_ID}-detail-title`
|
|
247
294
|
})]
|
|
248
295
|
});
|
|
249
296
|
}
|
|
@@ -253,16 +300,33 @@ export function JotaiModal({
|
|
|
253
300
|
onBack: handleBackFromHistory
|
|
254
301
|
}), /*#__PURE__*/_jsx(ModalHeader.Content, {
|
|
255
302
|
title: `${selectedHistoryAtom} History`,
|
|
256
|
-
centered: true
|
|
303
|
+
centered: true,
|
|
304
|
+
testID: `${JOTAI_TEST_ID}-history-title`
|
|
257
305
|
}), /*#__PURE__*/_jsx(ModalHeader.Actions, {
|
|
258
306
|
children: /*#__PURE__*/_jsx(ToolbarCopyButton, {
|
|
259
307
|
value: getHistorySnapshot,
|
|
260
308
|
disabled: activeChanges.length === 0,
|
|
261
|
-
buttonStyle: styles.headerActionButton
|
|
309
|
+
buttonStyle: styles.headerActionButton,
|
|
310
|
+
testID: `${JOTAI_TEST_ID}-copy`
|
|
262
311
|
})
|
|
263
312
|
})]
|
|
264
313
|
});
|
|
265
314
|
}
|
|
315
|
+
|
|
316
|
+
// Atom detail header — the live editor screen. AFTER history: history is
|
|
317
|
+
// reached FROM the detail screen and stacks over it, so when both are set
|
|
318
|
+
// history owns the header, and its back lands you back on the detail.
|
|
319
|
+
if (selectedAtomLabel) {
|
|
320
|
+
return /*#__PURE__*/_jsxs(ModalHeader, {
|
|
321
|
+
children: [/*#__PURE__*/_jsx(ModalHeader.Navigation, {
|
|
322
|
+
onBack: handleBackFromAtom
|
|
323
|
+
}), /*#__PURE__*/_jsx(ModalHeader.Content, {
|
|
324
|
+
title: selectedAtomLabel,
|
|
325
|
+
centered: true,
|
|
326
|
+
testID: `${JOTAI_TEST_ID}-atom-detail-title`
|
|
327
|
+
})]
|
|
328
|
+
});
|
|
329
|
+
}
|
|
266
330
|
return /*#__PURE__*/_jsxs(ModalHeader, {
|
|
267
331
|
children: [onBack && /*#__PURE__*/_jsx(ModalHeader.Navigation, {
|
|
268
332
|
onBack: onBack
|
|
@@ -270,27 +334,42 @@ export function JotaiModal({
|
|
|
270
334
|
title: "",
|
|
271
335
|
children: isSearchActive ? /*#__PURE__*/_jsxs(View, {
|
|
272
336
|
style: styles.headerSearchContainer,
|
|
337
|
+
testID: `${JOTAI_TEST_ID}-search`,
|
|
273
338
|
children: [/*#__PURE__*/_jsx(Search, {
|
|
274
339
|
size: 14,
|
|
275
340
|
color: night.color.textSecondary
|
|
276
341
|
}), /*#__PURE__*/_jsx(TextInput, {
|
|
277
342
|
ref: searchInputRef,
|
|
343
|
+
testID: `${JOTAI_TEST_ID}-search-field`,
|
|
278
344
|
style: styles.headerSearchInput,
|
|
279
345
|
placeholder: activeTab === "atoms" ? "Search atoms..." : "Search events...",
|
|
280
346
|
placeholderTextColor: night.color.placeholder,
|
|
281
347
|
value: searchText,
|
|
282
348
|
onChangeText: handleSearch,
|
|
283
|
-
onSubmitEditing: () =>
|
|
284
|
-
|
|
349
|
+
onSubmitEditing: () => searchInputRef.current?.blur()
|
|
350
|
+
// Stay open while the query is still narrowing the list.
|
|
351
|
+
//
|
|
352
|
+
// Collapsing on blur hid the reason rows were missing: the
|
|
353
|
+
// header went back to its TABS — whose counts are of the
|
|
354
|
+
// WHOLE capture — while the list stayed filtered, so the tool
|
|
355
|
+
// showed "Atoms (8)" above a single row and offered nothing to
|
|
356
|
+
// explain the gap. The only way back was to reopen a field the
|
|
357
|
+
// user had no reason to think was still armed. Same shape, and
|
|
358
|
+
// the same fix, as the Redux, Events and Zustand tools.
|
|
359
|
+
,
|
|
360
|
+
onBlur: () => {
|
|
361
|
+
if (searchText.length === 0) setIsSearchActive(false);
|
|
362
|
+
},
|
|
363
|
+
accessibilityLabel: "Search Jotai atoms and events",
|
|
285
364
|
autoCapitalize: "none",
|
|
286
365
|
autoCorrect: false,
|
|
287
366
|
returnKeyType: "search"
|
|
288
|
-
}),
|
|
289
|
-
onPress:
|
|
290
|
-
handleSearch("");
|
|
291
|
-
setIsSearchActive(false);
|
|
292
|
-
},
|
|
367
|
+
}), /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
368
|
+
onPress: closeSearch,
|
|
293
369
|
style: styles.headerSearchClear,
|
|
370
|
+
testID: `${JOTAI_TEST_ID}-search-clear`,
|
|
371
|
+
accessibilityRole: "button",
|
|
372
|
+
accessibilityLabel: "Close search",
|
|
294
373
|
children: /*#__PURE__*/_jsx(X, {
|
|
295
374
|
size: 14,
|
|
296
375
|
color: night.color.textSecondary
|
|
@@ -305,12 +384,16 @@ export function JotaiModal({
|
|
|
305
384
|
label: `Events${displayedChanges.length > 0 ? ` (${displayedChanges.length})` : ""}`
|
|
306
385
|
}],
|
|
307
386
|
activeTab: activeTab,
|
|
308
|
-
onTabChange: handleTabChange
|
|
387
|
+
onTabChange: handleTabChange,
|
|
388
|
+
testIDPrefix: `${JOTAI_TEST_ID}-tab`
|
|
309
389
|
})
|
|
310
390
|
}), /*#__PURE__*/_jsxs(ModalHeader.Actions, {
|
|
311
391
|
children: [/*#__PURE__*/_jsx(TouchableOpacity, {
|
|
312
392
|
onPress: () => setIsSearchActive(true),
|
|
313
393
|
style: styles.headerActionButton,
|
|
394
|
+
testID: `${JOTAI_TEST_ID}-search-open`,
|
|
395
|
+
accessibilityRole: "button",
|
|
396
|
+
accessibilityLabel: "Search",
|
|
314
397
|
children: /*#__PURE__*/_jsx(Search, {
|
|
315
398
|
size: 14,
|
|
316
399
|
color: night.color.textSecondary
|
|
@@ -318,6 +401,9 @@ export function JotaiModal({
|
|
|
318
401
|
}), /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
319
402
|
onPress: () => setShowFilters(true),
|
|
320
403
|
style: [styles.headerActionButton, hasActiveFilters && styles.headerActionButtonActive],
|
|
404
|
+
testID: `${JOTAI_TEST_ID}-filters-open`,
|
|
405
|
+
accessibilityRole: "button",
|
|
406
|
+
accessibilityLabel: "Filters",
|
|
321
407
|
children: /*#__PURE__*/_jsx(Filter, {
|
|
322
408
|
size: 14,
|
|
323
409
|
color: hasActiveFilters ? night.color.accent : night.color.textSecondary
|
|
@@ -325,20 +411,24 @@ export function JotaiModal({
|
|
|
325
411
|
}), activeTab === "atoms" && /*#__PURE__*/_jsx(ToolbarCopyButton, {
|
|
326
412
|
value: getAtomsSnapshot,
|
|
327
413
|
disabled: displayedAtoms.length === 0,
|
|
328
|
-
buttonStyle: styles.headerActionButton
|
|
414
|
+
buttonStyle: styles.headerActionButton,
|
|
415
|
+
testID: `${JOTAI_TEST_ID}-copy`
|
|
329
416
|
}), activeTab === "events" && /*#__PURE__*/_jsxs(_Fragment, {
|
|
330
417
|
children: [/*#__PURE__*/_jsx(ToolbarCopyButton, {
|
|
331
418
|
value: getEventsSnapshot,
|
|
332
419
|
disabled: displayedChanges.length === 0,
|
|
333
|
-
buttonStyle: styles.headerActionButton
|
|
420
|
+
buttonStyle: styles.headerActionButton,
|
|
421
|
+
testID: `${JOTAI_TEST_ID}-copy`
|
|
334
422
|
}), /*#__PURE__*/_jsx(PowerToggleButton, {
|
|
335
423
|
isEnabled: isEnabled,
|
|
336
424
|
onToggle: toggleCapture,
|
|
337
|
-
accessibilityLabel: "Toggle Jotai atom capture"
|
|
425
|
+
accessibilityLabel: "Toggle Jotai atom capture",
|
|
426
|
+
testID: `${JOTAI_TEST_ID}-power`
|
|
338
427
|
}), /*#__PURE__*/_jsx(ToolbarClearButton, {
|
|
339
428
|
onPress: clearChanges,
|
|
340
429
|
disabled: displayedChanges.length === 0,
|
|
341
|
-
buttonStyle: styles.headerActionButton
|
|
430
|
+
buttonStyle: styles.headerActionButton,
|
|
431
|
+
testID: `${JOTAI_TEST_ID}-clear`
|
|
342
432
|
})]
|
|
343
433
|
})]
|
|
344
434
|
})]
|
|
@@ -368,18 +458,20 @@ export function JotaiModal({
|
|
|
368
458
|
}
|
|
369
459
|
if (selectedHistoryAtom) {
|
|
370
460
|
return activeChanges.length > 0 ? /*#__PURE__*/_jsx(FlatList, {
|
|
461
|
+
testID: `${JOTAI_TEST_ID}-history-list`,
|
|
371
462
|
data: activeChanges,
|
|
372
463
|
renderItem: renderItem,
|
|
373
464
|
keyExtractor: keyExtractor,
|
|
374
465
|
contentContainerStyle: styles.listContent,
|
|
375
466
|
showsVerticalScrollIndicator: true,
|
|
376
|
-
removeClippedSubviews:
|
|
467
|
+
removeClippedSubviews: CLIP_OFFSCREEN_CELLS,
|
|
377
468
|
initialNumToRender: 12,
|
|
378
469
|
maxToRenderPerBatch: 8,
|
|
379
470
|
windowSize: 5,
|
|
380
471
|
updateCellsBatchingPeriod: 50
|
|
381
472
|
}) : /*#__PURE__*/_jsxs(View, {
|
|
382
473
|
style: styles.emptyState,
|
|
474
|
+
testID: `${JOTAI_TEST_ID}-history-empty`,
|
|
383
475
|
children: [/*#__PURE__*/_jsx(Box, {
|
|
384
476
|
size: 32,
|
|
385
477
|
color: night.color.textTertiary
|
|
@@ -392,12 +484,39 @@ export function JotaiModal({
|
|
|
392
484
|
})]
|
|
393
485
|
});
|
|
394
486
|
}
|
|
487
|
+
|
|
488
|
+
// Atom detail — live editor + details + read-only tree. After history in
|
|
489
|
+
// this chain for the same reason as the header: history stacks OVER the
|
|
490
|
+
// detail screen it was opened from.
|
|
491
|
+
if (selectedAtomLabel) {
|
|
492
|
+
const selectedAtom = atoms.find(a => a.label === selectedAtomLabel) ?? null;
|
|
493
|
+
return selectedAtom ? /*#__PURE__*/_jsx(JotaiAtomValueDetailContent, {
|
|
494
|
+
atom: selectedAtom,
|
|
495
|
+
onViewHistory: handleViewHistory
|
|
496
|
+
}) :
|
|
497
|
+
/*#__PURE__*/
|
|
498
|
+
// The atom fell out of the registry mid-look (a remote mirror
|
|
499
|
+
// rebuilding, a dev reload). Say so rather than rendering nothing.
|
|
500
|
+
_jsxs(View, {
|
|
501
|
+
style: styles.emptyState,
|
|
502
|
+
testID: `${JOTAI_TEST_ID}-atom-detail-gone`,
|
|
503
|
+
children: [/*#__PURE__*/_jsx(Box, {
|
|
504
|
+
size: 32,
|
|
505
|
+
color: night.color.textTertiary
|
|
506
|
+
}), /*#__PURE__*/_jsx(Text, {
|
|
507
|
+
style: styles.emptyTitle,
|
|
508
|
+
children: "Atom no longer registered"
|
|
509
|
+
}), /*#__PURE__*/_jsxs(Text, {
|
|
510
|
+
style: styles.emptyText,
|
|
511
|
+
children: [selectedAtomLabel, " is not in the registry any more."]
|
|
512
|
+
})]
|
|
513
|
+
});
|
|
514
|
+
}
|
|
395
515
|
if (activeTab === "atoms") {
|
|
396
516
|
return /*#__PURE__*/_jsx(JotaiAtomBrowser, {
|
|
397
517
|
atoms: displayedAtoms,
|
|
398
518
|
searchQuery: searchText,
|
|
399
|
-
|
|
400
|
-
onAtomExpand: onAtomExpand
|
|
519
|
+
onOpenAtom: handleOpenAtom
|
|
401
520
|
});
|
|
402
521
|
}
|
|
403
522
|
|
|
@@ -405,6 +524,7 @@ export function JotaiModal({
|
|
|
405
524
|
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
406
525
|
children: [!isEnabled && /*#__PURE__*/_jsxs(View, {
|
|
407
526
|
style: styles.disabledBanner,
|
|
527
|
+
testID: `${JOTAI_TEST_ID}-banner-disabled`,
|
|
408
528
|
children: [/*#__PURE__*/_jsx(Power, {
|
|
409
529
|
size: 14,
|
|
410
530
|
color: night.color.warning
|
|
@@ -416,8 +536,11 @@ export function JotaiModal({
|
|
|
416
536
|
style: styles.lockedBanner,
|
|
417
537
|
onPress: handleUpgradePress,
|
|
418
538
|
activeOpacity: 0.8,
|
|
539
|
+
testID: `${JOTAI_TEST_ID}-locked`,
|
|
540
|
+
accessibilityRole: "button",
|
|
419
541
|
children: [/*#__PURE__*/_jsxs(Text, {
|
|
420
542
|
style: styles.lockedText,
|
|
543
|
+
testID: `${JOTAI_TEST_ID}-locked-text`,
|
|
421
544
|
children: [lockedChangeCount, " older", " ", lockedChangeCount === 1 ? "change" : "changes", " locked"]
|
|
422
545
|
}), /*#__PURE__*/_jsx(View, {
|
|
423
546
|
style: styles.upgradeBadge,
|
|
@@ -428,12 +551,13 @@ export function JotaiModal({
|
|
|
428
551
|
})]
|
|
429
552
|
}), visibleChanges.length > 0 ? /*#__PURE__*/_jsx(FlatList, {
|
|
430
553
|
ref: flatListRef,
|
|
554
|
+
testID: `${JOTAI_TEST_ID}-list`,
|
|
431
555
|
data: visibleChanges,
|
|
432
556
|
renderItem: renderItem,
|
|
433
557
|
keyExtractor: keyExtractor,
|
|
434
558
|
contentContainerStyle: styles.listContent,
|
|
435
559
|
showsVerticalScrollIndicator: true,
|
|
436
|
-
removeClippedSubviews:
|
|
560
|
+
removeClippedSubviews: CLIP_OFFSCREEN_CELLS,
|
|
437
561
|
initialNumToRender: 12,
|
|
438
562
|
maxToRenderPerBatch: 8,
|
|
439
563
|
windowSize: 5,
|
|
@@ -455,7 +579,18 @@ export function JotaiModal({
|
|
|
455
579
|
|
|
456
580
|
// The change lists own their scrolling so FlatList virtualization works
|
|
457
581
|
// (see ZustandModal — same pattern, same tool-bench evidence).
|
|
458
|
-
|
|
582
|
+
//
|
|
583
|
+
// The ATOM BROWSER owns its scrolling too, which is not just symmetry. Its
|
|
584
|
+
// root is a `flex: 1` ScrollView, and a `flex: 1` box inside JsModal's
|
|
585
|
+
// wrapper (`contentContainerStyle: { flexGrow: 1 }`) resolves against the
|
|
586
|
+
// CONTENT rather than the viewport — so nested, the browser's scroller grows
|
|
587
|
+
// to the full height of its rows, never scrolls, and the wrapper scrolls
|
|
588
|
+
// instead. A person cannot tell the difference; automation can, because the
|
|
589
|
+
// scroller Detox is handed reports a frame taller than the sheet and every
|
|
590
|
+
// scroll start point it computes lands in the clipped part ("view is not
|
|
591
|
+
// scrollable at the given start point"). Owning the scroll gives the browser
|
|
592
|
+
// a bounded height and one scroller with a handle on it.
|
|
593
|
+
const listOwnsScroll = !showFilters && !(selectedChange && selectedChangeIndex !== null) && (selectedHistoryAtom != null || activeTab === "events" || activeTab === "atoms");
|
|
459
594
|
const body = /*#__PURE__*/_jsx(View, {
|
|
460
595
|
style: styles.container,
|
|
461
596
|
children: renderContent()
|
|
@@ -273,7 +273,8 @@ class JotaiStateStore {
|
|
|
273
273
|
label: atom.label,
|
|
274
274
|
changeCount: atom.changeCount,
|
|
275
275
|
color: atom.color,
|
|
276
|
-
currentValue
|
|
276
|
+
currentValue,
|
|
277
|
+
writeKind: atom.writeKind
|
|
277
278
|
};
|
|
278
279
|
});
|
|
279
280
|
}
|
|
@@ -328,7 +329,10 @@ class JotaiStateStore {
|
|
|
328
329
|
label: snapshot.label,
|
|
329
330
|
changeCount: snapshot.changeCount,
|
|
330
331
|
color: snapshot.color,
|
|
331
|
-
getValue: () => currentValue
|
|
332
|
+
getValue: () => currentValue,
|
|
333
|
+
// No setValue: the mirror has no wire back to the device from
|
|
334
|
+
// this entry (yet) — the detail screen renders read-only there.
|
|
335
|
+
writeKind: snapshot.writeKind
|
|
332
336
|
}];
|
|
333
337
|
}));
|
|
334
338
|
this.notifyListeners();
|
|
@@ -365,14 +369,15 @@ class JotaiStateStore {
|
|
|
365
369
|
|
|
366
370
|
// ---- Atom Registry ----
|
|
367
371
|
|
|
368
|
-
registerAtom(label, getValue, setValue) {
|
|
372
|
+
registerAtom(label, getValue, setValue, writeKind) {
|
|
369
373
|
if (this.atoms.has(label)) return;
|
|
370
374
|
const atomInfo = {
|
|
371
375
|
label,
|
|
372
376
|
changeCount: 0,
|
|
373
377
|
color: getAtomColor(label),
|
|
374
378
|
getValue,
|
|
375
|
-
setValue
|
|
379
|
+
setValue,
|
|
380
|
+
writeKind
|
|
376
381
|
};
|
|
377
382
|
this.atoms.set(label, atomInfo);
|
|
378
383
|
this.notifyAtomListeners();
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Jotai tool test handles.
|
|
5
|
+
*
|
|
6
|
+
* Every tappable and every value the e2e suite reads back is addressed
|
|
7
|
+
* `buoy-jotai-<thing>`. Two rules need explaining, and the second is the one
|
|
8
|
+
* this tool does not share with any other.
|
|
9
|
+
*
|
|
10
|
+
* **A change row's handle carries an ORDINAL, and it counts per ATOM.** Same
|
|
11
|
+
* reason as the Zustand tool: an atom a screen writes to fifty times produces
|
|
12
|
+
* fifty separate, equally real rows, and addressing them by label alone would
|
|
13
|
+
* give fifty views one handle — Detox fails a *matcher* that hits more than
|
|
14
|
+
* one element, so the suite would break on `toExist()` before any assertion,
|
|
15
|
+
* with an error that says nothing about duplicate changes. The handle is
|
|
16
|
+
* `buoy-jotai-row-<slug(atomLabel)>-<n>`, n counting back from the newest row
|
|
17
|
+
* for that atom (1 = newest). Deliberately relative: an atom change has no
|
|
18
|
+
* identity a host could know in advance, and what a test CAN know is "the
|
|
19
|
+
* write I just fired is the newest change to that atom". Read and tap in one
|
|
20
|
+
* beat — a row's ordinal moves the instant another change lands.
|
|
21
|
+
*
|
|
22
|
+
* **The ordinal is per ATOM, and one write can move several atoms.** This is
|
|
23
|
+
* where jotai differs from every store-shaped tool. Atoms form a dependency
|
|
24
|
+
* graph, and this tool watches NODES: a single `set` on a primitive atom
|
|
25
|
+
* notifies every mounted derived atom whose value actually moved, so one
|
|
26
|
+
* press can add a row under three different labels — and add nothing at all
|
|
27
|
+
* under a fourth whose recomputed value happened to be unchanged. Counting
|
|
28
|
+
* ordinals across the whole list would make a row's handle depend on how many
|
|
29
|
+
* *other* atoms the write happened to touch, which is exactly the thing a
|
|
30
|
+
* test of a graph must not assume.
|
|
31
|
+
*
|
|
32
|
+
* **An atom row's handle does NOT carry an ordinal.** An atom is registered
|
|
33
|
+
* once under a label that is unique by construction
|
|
34
|
+
* (`jotaiStateStore.registerAtom` returns early on a label it already holds,
|
|
35
|
+
* and `watchAtoms` marks the atom with a per-label symbol), so
|
|
36
|
+
* `buoy-jotai-atom-<slug(label)>` is as stable as the registration itself —
|
|
37
|
+
* and unlike a change, it is a thing the host knows the name of before the
|
|
38
|
+
* app has booted.
|
|
39
|
+
*
|
|
40
|
+
* The suite mirrors these rules in CommonJS (`example-dev-build/utils/
|
|
41
|
+
* jotaiBench.js`) and asserts the two agree — Detox runs in Node on the host
|
|
42
|
+
* and cannot import TypeScript.
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/** Prefix every handle in this tool shares. */
|
|
46
|
+
export const JOTAI_TEST_ID = "buoy-jotai";
|
|
47
|
+
|
|
48
|
+
/** `jbenchCount` → `jbenchcount`. Shared with every Buoy tool. */
|
|
49
|
+
export function slugForTestID(value) {
|
|
50
|
+
return String(value ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "unknown";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Handle for one change row: `buoy-jotai-row-jbenchcount-1`.
|
|
55
|
+
*
|
|
56
|
+
* `ordinal` is 1-based and counts back from the newest row for this atom.
|
|
57
|
+
*/
|
|
58
|
+
export function changeRowTestID(atomLabel, ordinal) {
|
|
59
|
+
return `${JOTAI_TEST_ID}-row-${slugForTestID(atomLabel)}-${ordinal}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Handle for one atom row in the browser: `buoy-jotai-atom-jbenchcount`. */
|
|
63
|
+
export function atomRowTestID(atomLabel) {
|
|
64
|
+
return `${JOTAI_TEST_ID}-atom-${slugForTestID(atomLabel)}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Row handle per change id for one rendered list, newest-first.
|
|
69
|
+
*
|
|
70
|
+
* Built once for the whole list rather than per row, because an ordinal is a
|
|
71
|
+
* fact about the list and a row cannot compute its own.
|
|
72
|
+
*/
|
|
73
|
+
export function buildChangeRowTestIDs(changes) {
|
|
74
|
+
const seen = new Map();
|
|
75
|
+
const handles = new Map();
|
|
76
|
+
for (const change of changes) {
|
|
77
|
+
const ordinal = (seen.get(change.atomLabel) ?? 0) + 1;
|
|
78
|
+
seen.set(change.atomLabel, ordinal);
|
|
79
|
+
handles.set(change.id, changeRowTestID(change.atomLabel, ordinal));
|
|
80
|
+
}
|
|
81
|
+
return handles;
|
|
82
|
+
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { jotaiStateStore } from "./jotaiStateStore";
|
|
13
|
+
import { classifyAtomWrite } from "./writeSemantics";
|
|
13
14
|
|
|
14
15
|
/** Any Jotai atom — alias for Atom<unknown> for readability */
|
|
15
16
|
|
|
@@ -60,7 +61,8 @@ export function watchAtoms(store, atoms, options) {
|
|
|
60
61
|
// function — primitive atoms get a default one, read-only derived atoms
|
|
61
62
|
// have none). Deriveds used to get a setter that threw inside jotai at
|
|
62
63
|
// set time; now they're correctly reported as read-only up front.
|
|
63
|
-
const
|
|
64
|
+
const isWritable = typeof atom.write === "function";
|
|
65
|
+
const setter = store.set && isWritable ? value => {
|
|
64
66
|
store.set(atom, value);
|
|
65
67
|
} : undefined;
|
|
66
68
|
jotaiStateStore.registerAtom(label, () => {
|
|
@@ -69,7 +71,11 @@ export function watchAtoms(store, atoms, options) {
|
|
|
69
71
|
} catch {
|
|
70
72
|
return undefined;
|
|
71
73
|
}
|
|
72
|
-
}, setter
|
|
74
|
+
}, setter,
|
|
75
|
+
// "value" = jotai's shared defaultWrite (a primitive atom: store.set
|
|
76
|
+
// stores the value). "action" = a custom write receiving the value as
|
|
77
|
+
// its argument — still writable, but the editor says so.
|
|
78
|
+
isWritable ? classifyAtomWrite(atom) : undefined);
|
|
73
79
|
|
|
74
80
|
// Record initial value
|
|
75
81
|
if (enabled) {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* What does `store.set(atom, x)` DO with x?
|
|
5
|
+
*
|
|
6
|
+
* jotai's `atom(initialValue)` gives every primitive atom the SAME shared
|
|
7
|
+
* `defaultWrite` function (see jotai/src/vanilla/atom.ts): it stores x as the
|
|
8
|
+
* atom's new value (or calls it with the previous value when x is a
|
|
9
|
+
* function). Any other `write` is app code — `atomWithReducer` receives
|
|
10
|
+
* ACTIONS, `atomWithStorage` persists as a side effect, a custom write can do
|
|
11
|
+
* anything — so a raw value handed to `store.set` may not mean "set to this".
|
|
12
|
+
*
|
|
13
|
+
* The tool's editor needs that distinction to be honest: a "value" atom edits
|
|
14
|
+
* like plain data; an "action" atom is still writable (the MCP `setAtom` and
|
|
15
|
+
* Time Machine restore already write to them) but the editor says what the
|
|
16
|
+
* write actually does.
|
|
17
|
+
*
|
|
18
|
+
* Detection: a probe primitive atom exposes this jotai instance's
|
|
19
|
+
* `defaultWrite`; identity match is exact. The `toString` fallback covers an
|
|
20
|
+
* app that somehow bundles a second jotai copy (its atoms carry a different —
|
|
21
|
+
* but textually identical — defaultWrite instance). An unrecognized write
|
|
22
|
+
* classifies as "action", which only makes the editor more cautious.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { atom as probeAtom } from "jotai/vanilla";
|
|
26
|
+
const probe = probeAtom(null);
|
|
27
|
+
const DEFAULT_WRITE = probe.write;
|
|
28
|
+
const DEFAULT_WRITE_SOURCE = typeof DEFAULT_WRITE === "function" ? String(DEFAULT_WRITE) : null;
|
|
29
|
+
|
|
30
|
+
/** Classify a writable atom's write. Call only when `atom.write` exists. */
|
|
31
|
+
export function classifyAtomWrite(candidate) {
|
|
32
|
+
const atomLike = candidate;
|
|
33
|
+
const write = atomLike?.write;
|
|
34
|
+
if (typeof write !== "function") return "action";
|
|
35
|
+
if (write === DEFAULT_WRITE) return "value";
|
|
36
|
+
// Dual-copy fallback: a primitive atom from another jotai instance holds
|
|
37
|
+
// its own value (`init` present) and its write's source is the library's
|
|
38
|
+
// defaultWrite verbatim.
|
|
39
|
+
if (atomLike !== null && "init" in atomLike && DEFAULT_WRITE_SOURCE !== null && String(write) === DEFAULT_WRITE_SOURCE) {
|
|
40
|
+
return "value";
|
|
41
|
+
}
|
|
42
|
+
return "action";
|
|
43
|
+
}
|
|
@@ -40,6 +40,7 @@ export type { UseJotaiAtomChangesResult } from "./jotai/hooks/useJotaiAtomChange
|
|
|
40
40
|
export { JotaiModal, JotaiToolView } from "./jotai/components/JotaiModal";
|
|
41
41
|
export { JotaiAtomChangeItem } from "./jotai/components/JotaiAtomChangeItem";
|
|
42
42
|
export { JotaiAtomDetailContent, JotaiAtomDetailFooter, } from "./jotai/components/JotaiAtomDetailContent";
|
|
43
|
+
export { JotaiAtomValueDetailContent } from "./jotai/components/JotaiAtomValueDetailContent";
|
|
43
44
|
export { JotaiIcon, JOTAI_ICON_COLOR } from "./jotai/components/JotaiIcon";
|
|
44
45
|
export { jotaiSyncAdapter } from "./jotai/sync/jotaiSyncAdapter";
|
|
45
46
|
export type { JotaiModalProps, JotaiDetailViewMode, JotaiAtomChange, JotaiAtomInfo, JotaiAtomSnapshot, JotaiFilter, AtomChangeCategory, WatchAtomsOptions, } from "./jotai/types";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,OAAO,gCAAgC,CAAC;AAKxC,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAK5D,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAKtD,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAKlE,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAK9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AACxE,YAAY,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAOnF,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAC;AAC7E,OAAO,EACL,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,2CAA2C,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAK3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAKjE,YAAY,EACV,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,WAAW,EACX,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAKvB,gBAAgB;AAChB,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,OAAO,gCAAgC,CAAC;AAKxC,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAK5D,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAKtD,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAKlE,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAK9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AACxE,YAAY,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAOnF,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAC;AAC7E,OAAO,EACL,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,2CAA2C,CAAC;AACnD,OAAO,EAAE,2BAA2B,EAAE,MAAM,gDAAgD,CAAC;AAC7F,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAK3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAKjE,YAAY,EACV,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,WAAW,EACX,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAKvB,gBAAgB;AAChB,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC"}
|
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
* JotaiAtomBrowser
|
|
3
3
|
*
|
|
4
4
|
* Atoms tab — shows all registered Jotai atoms and their current value.
|
|
5
|
-
* Mirrors ZustandStoreBrowser.tsx from @buoy-gg/zustand
|
|
5
|
+
* Mirrors ZustandStoreBrowser.tsx from @buoy-gg/zustand: tapping a card
|
|
6
|
+
* NAVIGATES to the atom's detail screen (live editor + details + read-only
|
|
7
|
+
* tree) rather than expanding in place — the modal owns that selection.
|
|
6
8
|
*/
|
|
7
9
|
import type { JotaiAtomInfo } from "../types";
|
|
8
10
|
interface JotaiAtomBrowserProps {
|
|
9
11
|
atoms: JotaiAtomInfo[];
|
|
10
12
|
searchQuery: string;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
onAtomExpand?: (label: string) => void;
|
|
13
|
+
/** Open an atom's detail screen (live editor). */
|
|
14
|
+
onOpenAtom: (label: string) => void;
|
|
14
15
|
}
|
|
15
|
-
export declare function JotaiAtomBrowser({ atoms, searchQuery,
|
|
16
|
+
export declare function JotaiAtomBrowser({ atoms, searchQuery, onOpenAtom, }: JotaiAtomBrowserProps): import("react").JSX.Element;
|
|
16
17
|
export {};
|
|
17
18
|
//# sourceMappingURL=JotaiAtomBrowser.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JotaiAtomBrowser.d.ts","sourceRoot":"","sources":["../../../../src/jotai/components/JotaiAtomBrowser.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"JotaiAtomBrowser.d.ts","sourceRoot":"","sources":["../../../../src/jotai/components/JotaiAtomBrowser.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAUH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAI9C,UAAU,qBAAqB;IAC7B,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC;AAyDD,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,WAAW,EACX,UAAU,GACX,EAAE,qBAAqB,+BAuFvB"}
|
|
@@ -7,6 +7,11 @@ import type { JotaiAtomChange } from "../types";
|
|
|
7
7
|
interface JotaiAtomChangeItemProps {
|
|
8
8
|
change: JotaiAtomChange;
|
|
9
9
|
onPress: (change: JotaiAtomChange) => void;
|
|
10
|
+
/**
|
|
11
|
+
* Row handle, built for the whole list by `buildChangeRowTestIDs` — a row
|
|
12
|
+
* carries an ordinal it cannot compute on its own. See ../utils/testIDs.ts.
|
|
13
|
+
*/
|
|
14
|
+
testID?: string;
|
|
10
15
|
}
|
|
11
16
|
export declare const JotaiAtomChangeItem: import("react").NamedExoticComponent<JotaiAtomChangeItemProps>;
|
|
12
17
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JotaiAtomChangeItem.d.ts","sourceRoot":"","sources":["../../../../src/jotai/components/JotaiAtomChangeItem.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAsB,MAAM,UAAU,CAAC;AAGpE,UAAU,wBAAwB;IAChC,MAAM,EAAE,eAAe,CAAC;IACxB,OAAO,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"JotaiAtomChangeItem.d.ts","sourceRoot":"","sources":["../../../../src/jotai/components/JotaiAtomChangeItem.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAsB,MAAM,UAAU,CAAC;AAGpE,UAAU,wBAAwB;IAChC,MAAM,EAAE,eAAe,CAAC;IACxB,OAAO,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IAC3C;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAsDD,eAAO,MAAM,mBAAmB,gEAoD9B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JotaiAtomDetailContent.d.ts","sourceRoot":"","sources":["../../../../src/jotai/components/JotaiAtomDetailContent.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAA+C,MAAM,OAAO,CAAC;AA0BpE,OAAO,KAAK,EACV,eAAe,EAEf,mBAAmB,EACpB,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"JotaiAtomDetailContent.d.ts","sourceRoot":"","sources":["../../../../src/jotai/components/JotaiAtomDetailContent.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAA+C,MAAM,OAAO,CAAC;AA0BpE,OAAO,KAAK,EACV,eAAe,EAEf,mBAAmB,EACpB,MAAM,UAAU,CAAC;AAmSlB,UAAU,2BAA2B;IACnC,MAAM,EAAE,eAAe,CAAC;IACxB,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;;;OAGG;IACH,WAAW,CAAC,EAAE,mBAAmB,CAAC;CACnC;AAED,wBAAgB,sBAAsB,CAAC,EACrC,MAAM,EACN,OAAO,EACP,aAAa,EACb,aAAa,EACb,qBAA6B,EAC7B,WAAsB,GACvB,EAAE,2BAA2B,qBAgL7B;AAED,wBAAgB,qBAAqB,CAAC,EACpC,MAAM,EACN,OAAO,EACP,aAAa,EACb,aAAa,GACd,EAAE;IACD,MAAM,EAAE,eAAe,CAAC;IACxB,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC,4BAcA"}
|