termcast 1.5.0 → 1.7.0
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/dist/build.d.ts.map +1 -1
- package/dist/build.js +22 -5
- package/dist/build.js.map +1 -1
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +7 -1
- package/dist/compile.js.map +1 -1
- package/dist/components/bar-chart.d.ts.map +1 -1
- package/dist/components/bar-chart.js +14 -3
- package/dist/components/bar-chart.js.map +1 -1
- package/dist/components/bar-graph.d.ts +4 -4
- package/dist/components/bar-graph.d.ts.map +1 -1
- package/dist/components/bar-graph.js +23 -5
- package/dist/components/bar-graph.js.map +1 -1
- package/dist/components/candle-chart.d.ts +15 -0
- package/dist/components/candle-chart.d.ts.map +1 -1
- package/dist/components/candle-chart.js +41 -3
- package/dist/components/candle-chart.js.map +1 -1
- package/dist/components/chart-tooltip.d.ts +83 -0
- package/dist/components/chart-tooltip.d.ts.map +1 -0
- package/dist/components/chart-tooltip.js +127 -0
- package/dist/components/chart-tooltip.js.map +1 -0
- package/dist/components/dotted-line-graph.d.ts +11 -0
- package/dist/components/dotted-line-graph.d.ts.map +1 -1
- package/dist/components/dotted-line-graph.js +43 -2
- package/dist/components/dotted-line-graph.js.map +1 -1
- package/dist/components/graph.d.ts +11 -0
- package/dist/components/graph.d.ts.map +1 -1
- package/dist/components/graph.js +53 -4
- package/dist/components/graph.js.map +1 -1
- package/dist/components/horizontal-bar-graph.d.ts.map +1 -1
- package/dist/components/horizontal-bar-graph.js +16 -5
- package/dist/components/horizontal-bar-graph.js.map +1 -1
- package/dist/components/list.d.ts +7 -0
- package/dist/components/list.d.ts.map +1 -1
- package/dist/components/list.js +75 -14
- package/dist/components/list.js.map +1 -1
- package/dist/examples/chart-tooltips.d.ts +2 -0
- package/dist/examples/chart-tooltips.d.ts.map +1 -0
- package/dist/examples/chart-tooltips.js +16 -0
- package/dist/examples/chart-tooltips.js.map +1 -0
- package/dist/examples/list-detail-height-ratchet.d.ts +2 -0
- package/dist/examples/list-detail-height-ratchet.d.ts.map +1 -0
- package/dist/examples/list-detail-height-ratchet.js +26 -0
- package/dist/examples/list-detail-height-ratchet.js.map +1 -0
- package/dist/extensions/dev.d.ts.map +1 -1
- package/dist/extensions/dev.js +1 -0
- package/dist/extensions/dev.js.map +1 -1
- package/dist/globals.js +8 -0
- package/dist/globals.js.map +1 -1
- package/dist/package-json.d.ts +2 -0
- package/dist/package-json.d.ts.map +1 -1
- package/dist/package-json.js +20 -17
- package/dist/package-json.js.map +1 -1
- package/dist/profiler.d.ts +2 -0
- package/dist/profiler.d.ts.map +1 -0
- package/dist/profiler.js +390 -0
- package/dist/profiler.js.map +1 -0
- package/package.json +14 -15
- package/src/build.tsx +27 -5
- package/src/cli.tsx +0 -0
- package/src/compile.tsx +9 -1
- package/src/compile.vitest.tsx +8 -8
- package/src/components/bar-chart.tsx +23 -3
- package/src/components/bar-graph.tsx +32 -13
- package/src/components/candle-chart.tsx +63 -16
- package/src/components/chart-tooltip.tsx +191 -0
- package/src/components/dotted-line-graph.tsx +49 -3
- package/src/components/graph.tsx +76 -18
- package/src/components/horizontal-bar-graph.tsx +24 -4
- package/src/components/list.tsx +93 -20
- package/src/examples/action-shortcut.vitest.tsx +4 -4
- package/src/examples/actions-context.vitest.tsx +2 -2
- package/src/examples/bar-graph-weekly.vitest.tsx +97 -97
- package/src/examples/chart-tooltips.tsx +54 -0
- package/src/examples/form-basic.vitest.tsx +8 -8
- package/src/examples/github.vitest.tsx +19 -28
- package/src/examples/graph-bar-chart.vitest.tsx +40 -40
- package/src/examples/graph-polymarket.vitest.tsx +24 -24
- package/src/examples/graph-row.vitest.tsx +8 -8
- package/src/examples/graph-styles.vitest.tsx +65 -65
- package/src/examples/horizontal-bar-graph-weekly.vitest.tsx +52 -52
- package/src/examples/list-detail-height-ratchet.tsx +48 -0
- package/src/examples/list-detail-height-ratchet.vitest.tsx +161 -0
- package/src/examples/list-detail-metadata.vitest.tsx +49 -49
- package/src/examples/list-dropdown-default.vitest.tsx +27 -27
- package/src/examples/list-fetch-data.vitest.tsx +3 -3
- package/src/examples/list-item-accessories.vitest.tsx +2 -2
- package/src/examples/list-loading-empty-view.vitest.tsx +1 -1
- package/src/examples/list-no-actions.vitest.tsx +3 -3
- package/src/examples/list-scrollbox.vitest.tsx +6 -6
- package/src/examples/list-spacing-mode.vitest.tsx +3 -3
- package/src/examples/list-with-detail.vitest.tsx +11 -11
- package/src/examples/list-with-dropdown.vitest.tsx +7 -7
- package/src/examples/list-with-sections.vitest.tsx +32 -32
- package/src/examples/list-with-toast.vitest.tsx +4 -4
- package/src/examples/simple-candle-chart.vitest.tsx +63 -61
- package/src/examples/simple-grid.vitest.tsx +13 -13
- package/src/examples/simple-navigation.vitest.tsx +25 -25
- package/src/examples/simple-progress-bar.vitest.tsx +8 -8
- package/src/examples/swift-extension.vitest.tsx +3 -3
- package/src/examples/toast-action.vitest.tsx +4 -4
- package/src/extensions/dev.tsx +2 -1
- package/src/extensions/dev.vitest.tsx +17 -17
- package/src/globals.ts +9 -0
- package/src/package-json.tsx +24 -23
- package/src/profiler.tsx +487 -0
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
* colored series rows and percentages.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import React, { ReactNode, useMemo } from 'react'
|
|
9
|
+
import React, { ReactNode, useMemo, useRef } from 'react'
|
|
10
10
|
import { BoxProps } from '@opentui/react'
|
|
11
|
+
import type { MouseEvent as OpenTUIMouseEvent } from '@opentui/core'
|
|
11
12
|
import { Color, resolveColor } from 'termcast/src/colors'
|
|
12
13
|
import { getThemePalette, useTheme } from 'termcast/src/theme'
|
|
14
|
+
import { ChartTooltip, useChartTooltip, formatTooltipLine } from 'termcast/src/components/chart-tooltip'
|
|
13
15
|
|
|
14
16
|
export interface HorizontalBarGraphSeriesProps {
|
|
15
17
|
/** One value per row/category position. */
|
|
@@ -97,6 +99,8 @@ const HorizontalBarGraph: HorizontalBarGraphType = (props) => {
|
|
|
97
99
|
} = props
|
|
98
100
|
|
|
99
101
|
const palette = getThemePalette(theme)
|
|
102
|
+
const containerRef = useRef<any>(null)
|
|
103
|
+
const { tooltip, show: showTooltip, hide: hideTooltip } = useChartTooltip()
|
|
100
104
|
|
|
101
105
|
const seriesList = useMemo<Array<{ data: number[]; color: string; title?: string }>>(() => {
|
|
102
106
|
const childArray = React.Children.toArray(children)
|
|
@@ -189,7 +193,8 @@ const HorizontalBarGraph: HorizontalBarGraphType = (props) => {
|
|
|
189
193
|
const chartHeight = headerHeight + visibleRows.length
|
|
190
194
|
|
|
191
195
|
return (
|
|
192
|
-
<box flexDirection="column" width="100%" flexShrink={0} {...rest}>
|
|
196
|
+
<box ref={containerRef} flexDirection="column" width="100%" flexShrink={0} {...rest} onMouseOut={hideTooltip}>
|
|
197
|
+
<ChartTooltip tooltip={tooltip} containerRef={containerRef} />
|
|
193
198
|
{showHeader && (
|
|
194
199
|
<>
|
|
195
200
|
<box flexDirection="row" height={1} flexShrink={0}>
|
|
@@ -238,7 +243,22 @@ const HorizontalBarGraph: HorizontalBarGraphType = (props) => {
|
|
|
238
243
|
}
|
|
239
244
|
const series = seriesList[seriesIndex]!
|
|
240
245
|
return (
|
|
241
|
-
<box
|
|
246
|
+
<box
|
|
247
|
+
key={seriesIndex}
|
|
248
|
+
flexGrow={value}
|
|
249
|
+
flexBasis={0}
|
|
250
|
+
flexShrink={1}
|
|
251
|
+
overflow="hidden"
|
|
252
|
+
onMouseMove={(evt: OpenTUIMouseEvent) => {
|
|
253
|
+
const label = row.label
|
|
254
|
+
const seriesTitle = series.title || `#${seriesIndex + 1}`
|
|
255
|
+
showTooltip({
|
|
256
|
+
x: evt.x,
|
|
257
|
+
y: evt.y,
|
|
258
|
+
lines: [label, formatTooltipLine(seriesTitle, value)],
|
|
259
|
+
})
|
|
260
|
+
}}
|
|
261
|
+
>
|
|
242
262
|
<box position="absolute" width="100%" height="100%" overflow="hidden">
|
|
243
263
|
<text fg={series.color} wrapMode="none">{barCharacter.repeat(200)}</text>
|
|
244
264
|
</box>
|
|
@@ -246,7 +266,7 @@ const HorizontalBarGraph: HorizontalBarGraphType = (props) => {
|
|
|
246
266
|
)
|
|
247
267
|
})}
|
|
248
268
|
{maxTotal > row.total && (
|
|
249
|
-
<box flexGrow={maxTotal - row.total} flexBasis={0} flexShrink={1} />
|
|
269
|
+
<box flexGrow={maxTotal - row.total} flexBasis={0} flexShrink={1} onMouseMove={hideTooltip} />
|
|
250
270
|
)}
|
|
251
271
|
</box>
|
|
252
272
|
</box>
|
package/src/components/list.tsx
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
TextareaRenderable,
|
|
7
7
|
} from '@opentui/core'
|
|
8
8
|
import type { MouseEvent as OpenTUIMouseEvent } from '@opentui/core'
|
|
9
|
-
import { useKeyboard, flushSync } from '@opentui/react'
|
|
9
|
+
import { useKeyboard, flushSync, useTerminalDimensions } from '@opentui/react'
|
|
10
10
|
import React, {
|
|
11
11
|
ReactElement,
|
|
12
12
|
ReactNode,
|
|
@@ -164,15 +164,7 @@ function ListFooter(): any {
|
|
|
164
164
|
</text>
|
|
165
165
|
</Hoverable>
|
|
166
166
|
)}
|
|
167
|
-
|
|
168
|
-
<Hoverable
|
|
169
|
-
onMouseDown={() => {
|
|
170
|
-
executeVimCommand('vim')
|
|
171
|
-
}}
|
|
172
|
-
>
|
|
173
|
-
<text flexShrink={0} fg={theme.textMuted}>:vim</text>
|
|
174
|
-
</Hoverable>
|
|
175
|
-
)}
|
|
167
|
+
|
|
176
168
|
</box>
|
|
177
169
|
)
|
|
178
170
|
|
|
@@ -225,8 +217,17 @@ function CurrentItemDetail(props: {
|
|
|
225
217
|
}): any {
|
|
226
218
|
const theme = useTheme()
|
|
227
219
|
const descendantsMap = useListDescendantsRerender()
|
|
228
|
-
|
|
229
|
-
|
|
220
|
+
const boxRef = React.useRef<BoxRenderable>(null)
|
|
221
|
+
// Grow-only height ratchet: once the detail panel reaches a certain height,
|
|
222
|
+
// it never shrinks below that. This prevents the footer from jumping up
|
|
223
|
+
// when navigating from a tall detail to a short one.
|
|
224
|
+
const maxHeightRef = React.useRef(0)
|
|
225
|
+
|
|
226
|
+
if (!props.isShowingDetail) {
|
|
227
|
+
// Reset ratchet when detail is hidden so next show starts fresh
|
|
228
|
+
maxHeightRef.current = 0
|
|
229
|
+
return null
|
|
230
|
+
}
|
|
230
231
|
|
|
231
232
|
const currentItem = Object.values(descendantsMap)
|
|
232
233
|
.find((item) => item.index === props.selectedIndex)
|
|
@@ -236,6 +237,14 @@ function CurrentItemDetail(props: {
|
|
|
236
237
|
|
|
237
238
|
return (
|
|
238
239
|
<box
|
|
240
|
+
ref={boxRef}
|
|
241
|
+
minHeight={maxHeightRef.current || undefined}
|
|
242
|
+
onSizeChange={() => {
|
|
243
|
+
const h = boxRef.current?.height ?? 0
|
|
244
|
+
if (h > maxHeightRef.current) {
|
|
245
|
+
maxHeightRef.current = h
|
|
246
|
+
}
|
|
247
|
+
}}
|
|
239
248
|
style={{
|
|
240
249
|
width: '50%',
|
|
241
250
|
paddingLeft: 1,
|
|
@@ -406,6 +415,13 @@ export interface ListProps
|
|
|
406
415
|
searchBarPlaceholder?: string
|
|
407
416
|
selectedItemId?: string
|
|
408
417
|
isShowingDetail?: boolean
|
|
418
|
+
/**
|
|
419
|
+
* Minimum terminal width in columns required to show the detail panel.
|
|
420
|
+
* When the terminal is narrower than this value, the detail panel is
|
|
421
|
+
* automatically hidden even if `isShowingDetail` is true.
|
|
422
|
+
* @default 80
|
|
423
|
+
*/
|
|
424
|
+
detailMinWidth?: number
|
|
409
425
|
/**
|
|
410
426
|
* Controls the vertical spacing of list items.
|
|
411
427
|
* - 'default': Single-line items with title and subtitle on same row
|
|
@@ -1060,6 +1076,7 @@ export const List: ListType = (props) => {
|
|
|
1060
1076
|
isLoading,
|
|
1061
1077
|
navigationTitle,
|
|
1062
1078
|
isShowingDetail,
|
|
1079
|
+
detailMinWidth = 80,
|
|
1063
1080
|
selectedItemId,
|
|
1064
1081
|
searchBarAccessory,
|
|
1065
1082
|
logo,
|
|
@@ -1070,6 +1087,9 @@ export const List: ListType = (props) => {
|
|
|
1070
1087
|
} = props
|
|
1071
1088
|
|
|
1072
1089
|
const theme = useTheme()
|
|
1090
|
+
const { width: terminalWidth } = useTerminalDimensions()
|
|
1091
|
+
const effectiveIsShowingDetail = isShowingDetail && terminalWidth >= detailMinWidth
|
|
1092
|
+
|
|
1073
1093
|
const currentStackSelectedListIndex = useStore((state) => {
|
|
1074
1094
|
const stack = state.navigationStack
|
|
1075
1095
|
const currentItem = stack[stack.length - 1]
|
|
@@ -1262,14 +1282,14 @@ export const List: ListType = (props) => {
|
|
|
1262
1282
|
setSelectedIndex: setSelectedIndexWithPersistence,
|
|
1263
1283
|
searchText,
|
|
1264
1284
|
isFiltering: isFilteringEnabled,
|
|
1265
|
-
isShowingDetail,
|
|
1285
|
+
isShowingDetail: effectiveIsShowingDetail,
|
|
1266
1286
|
customEmptyViewRef,
|
|
1267
1287
|
isLoading,
|
|
1268
1288
|
hasDropdown: !!searchBarAccessory,
|
|
1269
1289
|
spacingMode,
|
|
1270
1290
|
accessoryTagWidths: accessoryTagsLayout,
|
|
1271
1291
|
}),
|
|
1272
|
-
[isDropdownOpen, selectedIndex, searchText, isFilteringEnabled,
|
|
1292
|
+
[isDropdownOpen, selectedIndex, searchText, isFilteringEnabled, effectiveIsShowingDetail, isLoading, searchBarAccessory, spacingMode, accessoryTagsLayout],
|
|
1273
1293
|
)
|
|
1274
1294
|
|
|
1275
1295
|
// Handle selectedItemId prop changes (before paint to avoid flash)
|
|
@@ -1356,6 +1376,46 @@ export const List: ListType = (props) => {
|
|
|
1356
1376
|
}
|
|
1357
1377
|
}
|
|
1358
1378
|
|
|
1379
|
+
// Trigger pagination when the user mouse-scrolls near the bottom of the list.
|
|
1380
|
+
// Only fires on scroll-down so scrolling up near the bottom doesn't spuriously
|
|
1381
|
+
// re-trigger onLoadMore. Uses queueMicrotask because opentui calls onMouseScroll
|
|
1382
|
+
// before ScrollBox.onMouseEvent updates scrollTop in the same call stack;
|
|
1383
|
+
// a microtask runs after the synchronous handler chain finishes.
|
|
1384
|
+
const checkScrollPagination = (event: OpenTUIMouseEvent) => {
|
|
1385
|
+
if (event.scroll?.direction !== 'down') return
|
|
1386
|
+
|
|
1387
|
+
queueMicrotask(() => {
|
|
1388
|
+
const scrollBox = scrollBoxRef.current
|
|
1389
|
+
if (!scrollBox || !props.pagination?.hasMore) return
|
|
1390
|
+
|
|
1391
|
+
// Reset pagination lock when new items arrive (same logic as in move())
|
|
1392
|
+
const items = Object.values(descendantsContext.map.current)
|
|
1393
|
+
.filter((item) => item.index !== -1 && item.props?.visible !== false)
|
|
1394
|
+
if (items.length !== prevItemCountRef.current) {
|
|
1395
|
+
prevItemCountRef.current = items.length
|
|
1396
|
+
paginationCalledRef.current = false
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
if (paginationCalledRef.current) return
|
|
1400
|
+
|
|
1401
|
+
const scrollTop = scrollBox.scrollTop || 0
|
|
1402
|
+
const viewportHeight = scrollBox.viewport?.height || 0
|
|
1403
|
+
const contentHeight = scrollBox.scrollHeight || 0
|
|
1404
|
+
|
|
1405
|
+
// Nothing to paginate if content fits in viewport
|
|
1406
|
+
if (contentHeight <= viewportHeight) return
|
|
1407
|
+
|
|
1408
|
+
// Trigger when within 20% of the bottom (or 3 rows, whichever is larger)
|
|
1409
|
+
const threshold = Math.max(3, Math.floor(viewportHeight * 0.2))
|
|
1410
|
+
const distanceFromBottom = contentHeight - (scrollTop + viewportHeight)
|
|
1411
|
+
|
|
1412
|
+
if (distanceFromBottom <= threshold) {
|
|
1413
|
+
paginationCalledRef.current = true
|
|
1414
|
+
props.pagination.onLoadMore()
|
|
1415
|
+
}
|
|
1416
|
+
})
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1359
1419
|
const move = (direction: -1 | 1) => {
|
|
1360
1420
|
// Get all visible items
|
|
1361
1421
|
const items = Object.values(descendantsContext.map.current)
|
|
@@ -1612,7 +1672,8 @@ export const List: ListType = (props) => {
|
|
|
1612
1672
|
return
|
|
1613
1673
|
}
|
|
1614
1674
|
|
|
1615
|
-
// Ctrl+d
|
|
1675
|
+
// Ctrl+d / Ctrl+u for half-page down/up
|
|
1676
|
+
// Ctrl+f / Ctrl+b for full-page down/up
|
|
1616
1677
|
if (evt.ctrl && evt.name === 'd') {
|
|
1617
1678
|
const viewportHeight = scrollBoxRef.current?.viewport?.height || 20
|
|
1618
1679
|
moveByN(Math.floor(viewportHeight / 2))
|
|
@@ -1625,6 +1686,18 @@ export const List: ListType = (props) => {
|
|
|
1625
1686
|
evt.stopPropagation()
|
|
1626
1687
|
return
|
|
1627
1688
|
}
|
|
1689
|
+
if (evt.ctrl && evt.name === 'f') {
|
|
1690
|
+
const viewportHeight = scrollBoxRef.current?.viewport?.height || 20
|
|
1691
|
+
moveByN(viewportHeight)
|
|
1692
|
+
evt.stopPropagation()
|
|
1693
|
+
return
|
|
1694
|
+
}
|
|
1695
|
+
if (evt.ctrl && evt.name === 'b') {
|
|
1696
|
+
const viewportHeight = scrollBoxRef.current?.viewport?.height || 20
|
|
1697
|
+
moveByN(-viewportHeight)
|
|
1698
|
+
evt.stopPropagation()
|
|
1699
|
+
return
|
|
1700
|
+
}
|
|
1628
1701
|
|
|
1629
1702
|
// / to enter search mode
|
|
1630
1703
|
if (evt.sequence === '/' && !evt.ctrl && !evt.meta) {
|
|
@@ -1799,14 +1872,15 @@ export const List: ListType = (props) => {
|
|
|
1799
1872
|
{/* Main content area with optional detail view */}
|
|
1800
1873
|
<box style={{ flexDirection: 'row', flexGrow: 1, flexShrink: 1 }}>
|
|
1801
1874
|
{/* List content - render children which will register themselves */}
|
|
1802
|
-
<box style={{ width:
|
|
1875
|
+
<box style={{ width: effectiveIsShowingDetail ? '50%' : '100%', flexGrow: 1, flexShrink: 1, flexDirection: 'column' }}>
|
|
1803
1876
|
{/* Scrollable list items */}
|
|
1804
1877
|
<ScrollBox
|
|
1805
1878
|
ref={scrollBoxRef}
|
|
1806
1879
|
focused={false}
|
|
1807
1880
|
flexGrow={1}
|
|
1808
1881
|
flexShrink={1}
|
|
1809
|
-
minHeight={
|
|
1882
|
+
minHeight={10}
|
|
1883
|
+
onMouseScroll={checkScrollPagination}
|
|
1810
1884
|
style={{
|
|
1811
1885
|
rootOptions: {
|
|
1812
1886
|
backgroundColor: undefined,
|
|
@@ -1836,7 +1910,7 @@ export const List: ListType = (props) => {
|
|
|
1836
1910
|
{/* Detail panel on the right */}
|
|
1837
1911
|
<CurrentItemDetail
|
|
1838
1912
|
selectedIndex={selectedIndex}
|
|
1839
|
-
isShowingDetail={
|
|
1913
|
+
isShowingDetail={effectiveIsShowingDetail}
|
|
1840
1914
|
/>
|
|
1841
1915
|
</box>
|
|
1842
1916
|
</box>
|
|
@@ -1990,8 +2064,7 @@ const ListItem: ListItemType = (props) => {
|
|
|
1990
2064
|
}
|
|
1991
2065
|
}
|
|
1992
2066
|
|
|
1993
|
-
|
|
1994
|
-
const showAccessories = !props.detail && props.accessories
|
|
2067
|
+
const showAccessories = Boolean(props.accessories)
|
|
1995
2068
|
|
|
1996
2069
|
// Get icon string and color from props.icon (can be string or object with value/tintColor)
|
|
1997
2070
|
const { iconValue, iconColor } = (() => {
|
|
@@ -49,7 +49,7 @@ test('ctrl+r shortcut should trigger Refresh action directly', async () => {
|
|
|
49
49
|
|
|
50
50
|
> Search...
|
|
51
51
|
|
|
52
|
-
›
|
|
52
|
+
›Refres...unt: 1Press ctrl+r to refresh...ter then select Refresh
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
|
|
@@ -57,11 +57,11 @@ test('ctrl+r shortcut should trigger Refresh action directly', async () => {
|
|
|
57
57
|
|
|
58
58
|
|
|
59
59
|
|
|
60
|
-
↵ refresh ↑↓ navigate ^k actions :vim
|
|
61
60
|
|
|
62
61
|
|
|
63
62
|
|
|
64
63
|
|
|
64
|
+
↵ refresh ↑↓ navigate ^k actions
|
|
65
65
|
"
|
|
66
66
|
`)
|
|
67
67
|
}, 30000)
|
|
@@ -85,6 +85,7 @@ test('action shortcut is displayed in action panel', async () => {
|
|
|
85
85
|
expect(actionsPanel).toMatchInlineSnapshot(`
|
|
86
86
|
"
|
|
87
87
|
|
|
88
|
+
|
|
88
89
|
╭────────────────────────────────────────────────────────────────╮
|
|
89
90
|
│ │
|
|
90
91
|
│ Actions esc │
|
|
@@ -102,8 +103,7 @@ test('action shortcut is displayed in action panel', async () => {
|
|
|
102
103
|
│ │
|
|
103
104
|
│ │
|
|
104
105
|
│ │
|
|
105
|
-
│ ↵ select ↑↓ navigate │
|
|
106
|
-
│ │"
|
|
106
|
+
│ ↵ select ↑↓ navigate │"
|
|
107
107
|
`)
|
|
108
108
|
}, 30000)
|
|
109
109
|
|
|
@@ -38,6 +38,7 @@ test('actions preserve React context through portal', async () => {
|
|
|
38
38
|
expect(actionsPanel).toMatchInlineSnapshot(`
|
|
39
39
|
"
|
|
40
40
|
|
|
41
|
+
|
|
41
42
|
╭────────────────────────────────────────────────────────────────╮
|
|
42
43
|
│ │
|
|
43
44
|
│ Actions esc │
|
|
@@ -55,8 +56,7 @@ test('actions preserve React context through portal', async () => {
|
|
|
55
56
|
│ │
|
|
56
57
|
│ │
|
|
57
58
|
│ │
|
|
58
|
-
│ ↵ select ↑↓ navigate │
|
|
59
|
-
│ │"
|
|
59
|
+
│ ↵ select ↑↓ navigate │"
|
|
60
60
|
`)
|
|
61
61
|
|
|
62
62
|
// Select "Show Counter" (first action, already selected)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// E2E tests for BarGraph vertical stacked bar chart.
|
|
2
|
-
// Bar segments use
|
|
2
|
+
// Bar segments use █ (full block) chars for solid, gap-free columns.
|
|
3
3
|
|
|
4
4
|
import { test, expect, afterEach, beforeEach } from 'vitest'
|
|
5
5
|
import { launchTerminal, Session } from 'tuistory/src'
|
|
@@ -35,28 +35,28 @@ test('bar graph renders bars, labels, and legend', async () => {
|
|
|
35
35
|
|
|
36
36
|
> Search...
|
|
37
37
|
|
|
38
|
-
›
|
|
39
|
-
|
|
40
|
-
Server Load CPU / Memory / IO │ 82.5
|
|
41
|
-
Many ...
|
|
42
|
-
Many Series (8) Legend overflow test │ 55.0
|
|
43
|
-
|
|
44
|
-
Week 1 vs Week 2 Two graphs in a Row │ 27.5
|
|
45
|
-
│
|
|
46
|
-
│ 0.0
|
|
47
|
-
│ Mon Tue Wed Thu
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
38
|
+
›Weekly Traffi 3 channel...oss 6 days │ 110.0│ ██
|
|
39
|
+
Revenue by Regio EMEA / A...Americas │ │██ ██ ██ ██
|
|
40
|
+
Server Load CPU / Memory / IO │ 82.5│██ ██ ██ ██
|
|
41
|
+
Many C...ns (20)Overflow...h 20 bars │ │██ ██ ██ ██ ██
|
|
42
|
+
Many Series (8) Legend overflow test │ 55.0│██ ██ ██ ██ ██ ██
|
|
43
|
+
Long Label Labels wide...bar columns │ │██ ██ ██ ██ ██ ██
|
|
44
|
+
Week 1 vs Week 2 Two graphs in a Row │ 27.5│██ ██ ██ ██ ██ ██
|
|
45
|
+
│ │██ ██ ██ ██ ██ ██
|
|
46
|
+
│ 0.0│██ ██ ██ ██ ██ ██
|
|
47
|
+
│ Mon Tue Wed Thu FriSat
|
|
48
|
+
│ ■ Direct ■ Organic ■ Referral
|
|
49
|
+
│
|
|
50
|
+
│
|
|
51
|
+
│
|
|
52
|
+
│
|
|
53
|
+
│
|
|
54
|
+
│
|
|
55
|
+
│
|
|
56
|
+
│
|
|
57
|
+
│
|
|
58
|
+
│
|
|
59
|
+
↵ open detail ↑↓ navigate ^k act │
|
|
60
60
|
|
|
61
61
|
"
|
|
62
62
|
`)
|
|
@@ -64,7 +64,7 @@ test('bar graph renders bars, labels, and legend', async () => {
|
|
|
64
64
|
expect(text).toContain('Mon')
|
|
65
65
|
expect(text).toContain('Direct')
|
|
66
66
|
expect(text).toContain('0.0│')
|
|
67
|
-
expect(text).toContain('
|
|
67
|
+
expect(text).toContain('█')
|
|
68
68
|
}, 30000)
|
|
69
69
|
|
|
70
70
|
test('many columns (20) clips with overflow hidden', async () => {
|
|
@@ -75,7 +75,7 @@ test('many columns (20) clips with overflow hidden', async () => {
|
|
|
75
75
|
session.sendKey('down')
|
|
76
76
|
|
|
77
77
|
await session.text({
|
|
78
|
-
waitFor: (t) => t.includes('›Many
|
|
78
|
+
waitFor: (t) => t.includes('›Many') && t.includes('(20)'),
|
|
79
79
|
timeout: 10000,
|
|
80
80
|
})
|
|
81
81
|
await session.waitIdle()
|
|
@@ -83,13 +83,13 @@ test('many columns (20) clips with overflow hidden', async () => {
|
|
|
83
83
|
|
|
84
84
|
// Bar graph rendering has non-deterministic ANSI highlights, so use toContain checks
|
|
85
85
|
// instead of inline snapshot for the bars area
|
|
86
|
-
expect(text).toContain('›Many
|
|
86
|
+
expect(text).toContain('›Many')
|
|
87
87
|
expect(text).toContain('BarGraph Showcase')
|
|
88
|
-
expect(text).toContain('
|
|
88
|
+
expect(text).toContain('█')
|
|
89
89
|
|
|
90
90
|
// Some labels visible, overflow clips the rest
|
|
91
91
|
expect(text).toContain('D')
|
|
92
|
-
expect(text).toContain('
|
|
92
|
+
expect(text).toContain('█')
|
|
93
93
|
}, 30000)
|
|
94
94
|
|
|
95
95
|
test('many series (8) bottom legend clips on one row', async () => {
|
|
@@ -113,28 +113,28 @@ test('many series (8) bottom legend clips on one row', async () => {
|
|
|
113
113
|
|
|
114
114
|
> Search...
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
Server Load CPU / Memory / IO │ 246.0
|
|
119
|
-
Many ...
|
|
120
|
-
›Many Series (8) Legend overflow test │ 164.0
|
|
121
|
-
|
|
122
|
-
Week 1 vs Week 2 Two graphs in a Row │ 82.0
|
|
123
|
-
│
|
|
124
|
-
│ 0.0
|
|
125
|
-
│ Mon Tue Wed Thu
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
116
|
+
Weekly Traffi 3 channel...oss 6 days │ 328.0│██ ██ ██ ██ ██
|
|
117
|
+
Revenue by Regio EMEA / A...Americas │ │██ ██ ██ ██ ██ ██
|
|
118
|
+
Server Load CPU / Memory / IO │ 246.0│██ ██ ██ ██ ██ ██
|
|
119
|
+
Many C...ns (20)Overflow...h 20 bars │ │██ ██ ██ ██ ██ ██
|
|
120
|
+
›Many Series (8) Legend overflow test │ 164.0│██ ██ ██ ██ ██ ██
|
|
121
|
+
Long Label Labels wide...bar columns │ │██ ██ ██ ██ ██ ██
|
|
122
|
+
Week 1 vs Week 2 Two graphs in a Row │ 82.0│██ ██ ██ ██ ██ ██
|
|
123
|
+
│ │██ ██ ██ ██ ██ ██
|
|
124
|
+
│ 0.0│██ ██ ██ ██ ██ ██
|
|
125
|
+
│ Mon Tue Wed Thu FriSat
|
|
126
|
+
│ ■ Series 1 ■ Series 2 ■ Series 3
|
|
127
|
+
│
|
|
128
|
+
│
|
|
129
|
+
│
|
|
130
|
+
│
|
|
131
|
+
│
|
|
132
|
+
│
|
|
133
|
+
│
|
|
134
|
+
│
|
|
135
|
+
│
|
|
136
|
+
│
|
|
137
|
+
↑↓ navigate ^k actions │
|
|
138
138
|
|
|
139
139
|
"
|
|
140
140
|
`)
|
|
@@ -142,11 +142,11 @@ test('many series (8) bottom legend clips on one row', async () => {
|
|
|
142
142
|
// Bottom legend is a single clipped row by default.
|
|
143
143
|
expect(text).toContain('Series 1')
|
|
144
144
|
expect(text).toContain('Series 3')
|
|
145
|
-
expect(text).toContain('
|
|
145
|
+
expect(text).toContain('█')
|
|
146
146
|
}, 30000)
|
|
147
147
|
|
|
148
148
|
test('long labels truncated by overflow hidden', async () => {
|
|
149
|
-
await session.text({ waitFor: (t) => t.includes('
|
|
149
|
+
await session.text({ waitFor: (t) => t.includes('Labels wide'), timeout: 10000 })
|
|
150
150
|
// Navigate: Weekly, Revenue, Server, Many Columns, Many Series, Long Labels = 5 downs
|
|
151
151
|
session.sendKey('down')
|
|
152
152
|
session.sendKey('down')
|
|
@@ -155,7 +155,7 @@ test('long labels truncated by overflow hidden', async () => {
|
|
|
155
155
|
session.sendKey('down')
|
|
156
156
|
|
|
157
157
|
const text = await session.text({
|
|
158
|
-
waitFor: (t) => t.includes('›Lon...bels'),
|
|
158
|
+
waitFor: (t) => t.includes('›Long Label') || t.includes('›Lon...bels'),
|
|
159
159
|
timeout: 10000,
|
|
160
160
|
})
|
|
161
161
|
|
|
@@ -167,34 +167,34 @@ test('long labels truncated by overflow hidden', async () => {
|
|
|
167
167
|
|
|
168
168
|
> Search...
|
|
169
169
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
Server Load CPU / Memory / IO │ 56.3
|
|
173
|
-
Many ...
|
|
174
|
-
Many Series (8) Legend overflow test │ 37.5
|
|
175
|
-
›
|
|
176
|
-
Week 1 vs Week 2 Two graphs in a Row │ 18.8
|
|
177
|
-
│
|
|
178
|
-
│ 0.0
|
|
170
|
+
Weekly Traffi 3 channel...oss 6 days │ 75.0│ ██
|
|
171
|
+
Revenue by Regio EMEA / A...Americas │ │██ ██ ██
|
|
172
|
+
Server Load CPU / Memory / IO │ 56.3│██ ██ ██ ██
|
|
173
|
+
Many C...ns (20)Overflow...h 20 bars │ │██ ██ ██ ██
|
|
174
|
+
Many Series (8) Legend overflow test │ 37.5│██ ██ ██ ██ ██
|
|
175
|
+
›Long Label Labels wide...bar columns │ │██ ██ ██ ██ ██ ██
|
|
176
|
+
Week 1 vs Week 2 Two graphs in a Row │ 18.8│██ ██ ██ ██ ██ ██
|
|
177
|
+
│ │██ ██ ██ ██ ██ ██
|
|
178
|
+
│ 0.0│██ ██ ██ ██ ██ ██
|
|
179
179
|
│ Monday Thursday
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
180
|
+
│ ■ Views ■ Clicks
|
|
181
|
+
│
|
|
182
|
+
│
|
|
183
|
+
│
|
|
184
|
+
│
|
|
185
|
+
│
|
|
186
|
+
│
|
|
187
|
+
│
|
|
188
|
+
│
|
|
189
|
+
│
|
|
190
|
+
│
|
|
191
|
+
↑↓ navigate ^k actions │
|
|
192
192
|
|
|
193
193
|
"
|
|
194
194
|
`)
|
|
195
195
|
|
|
196
|
-
expect(text).toContain('
|
|
197
|
-
expect(text).toContain('
|
|
196
|
+
expect(text).toContain('Labels wide')
|
|
197
|
+
expect(text).toContain('█')
|
|
198
198
|
}, 30000)
|
|
199
199
|
|
|
200
200
|
test('side-by-side bar graphs in a Row', async () => {
|
|
@@ -220,28 +220,28 @@ test('side-by-side bar graphs in a Row', async () => {
|
|
|
220
220
|
|
|
221
221
|
> Search...
|
|
222
222
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
Server Load CPU / Memory / IO │ 82.5
|
|
226
|
-
Many ...
|
|
227
|
-
Many Series (8) Legend overflow test │ 55.0
|
|
228
|
-
|
|
229
|
-
›Week 1 vs Week 2 Two graphs in a Row │ 27.5
|
|
230
|
-
│
|
|
231
|
-
│ 0.0
|
|
223
|
+
Weekly Traffi 3 channel...oss 6 days │ 110.0│ 130.0│
|
|
224
|
+
Revenue by Regio EMEA / A...Americas │ │██ ██ │██
|
|
225
|
+
Server Load CPU / Memory / IO │ 82.5│██ ██ 97.5│██ ██
|
|
226
|
+
Many C...ns (20)Overflow...h 20 bars │ │██ ██ ██ │██ ██ ██
|
|
227
|
+
Many Series (8) Legend overflow test │ 55.0│██ ██ ██ 65.0│██ ██ ██
|
|
228
|
+
Long Label Labels wide...bar columns │ │██ ██ ██ │██ ██ ██
|
|
229
|
+
›Week 1 vs Week 2 Two graphs in a Row │ 27.5│██ ██ ██ 32.5│██ ██ ██
|
|
230
|
+
│ │██ ██ ██ │██ ██ ██
|
|
231
|
+
│ 0.0│██ ██ ██ 0.0│██ ██ ██
|
|
232
232
|
│ Mon Tue Wed Mon Tue We
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
233
|
+
│ ■ Direct ■ Organ ■ Direct ■ Orga
|
|
234
|
+
│
|
|
235
|
+
│
|
|
236
|
+
│
|
|
237
|
+
│
|
|
238
|
+
│
|
|
239
|
+
│
|
|
240
|
+
│
|
|
241
|
+
│
|
|
242
|
+
│
|
|
243
|
+
│
|
|
244
|
+
↵ open detail ↑↓ navigate ^k act │
|
|
245
245
|
|
|
246
246
|
"
|
|
247
247
|
`)
|