forked from Shopify/cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request Shopify#2364 from Shopify/prompt-scrollbar
Prompt scrollbar
- Loading branch information
Showing
12 changed files
with
436 additions
and
277 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@shopify/cli-kit': minor | ||
--- | ||
|
||
Add scrollbar to prompts |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
369 changes: 180 additions & 189 deletions
369
packages/cli-kit/src/private/node/ui/components/AutocompletePrompt.test.tsx
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
106 changes: 106 additions & 0 deletions
106
packages/cli-kit/src/private/node/ui/components/Scrollbar.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import {Scrollbar} from './Scrollbar.js' | ||
import {render} from '../../testing/ui.js' | ||
import {describe, expect, test} from 'vitest' | ||
import React from 'react' | ||
|
||
describe('Scrollbar', async () => { | ||
test('renders correctly when at the top of the list', async () => { | ||
const options = { | ||
containerHeight: 10, | ||
visibleListSectionLength: 10, | ||
fullListLength: 50, | ||
visibleFromIndex: 0, | ||
} | ||
|
||
const {lastFrame} = render(<Scrollbar {...options} />) | ||
|
||
// First 2 are colored in | ||
expect(lastFrame()).toMatchInlineSnapshot(` | ||
"\u001b[46m \u001b[49m | ||
\u001b[46m \u001b[49m | ||
\u001b[100m \u001b[49m | ||
\u001b[100m \u001b[49m | ||
\u001b[100m \u001b[49m | ||
\u001b[100m \u001b[49m | ||
\u001b[100m \u001b[49m | ||
\u001b[100m \u001b[49m | ||
\u001b[100m \u001b[49m | ||
\u001b[100m \u001b[49m" | ||
`) | ||
}) | ||
|
||
test('renders correctly when in the middle of the list', async () => { | ||
const options = { | ||
containerHeight: 10, | ||
visibleListSectionLength: 10, | ||
fullListLength: 50, | ||
visibleFromIndex: 20, | ||
} | ||
|
||
const {lastFrame} = render(<Scrollbar {...options} />) | ||
|
||
// Scrollbar is in the middle | ||
expect(lastFrame()).toMatchInlineSnapshot(` | ||
"\u001b[100m \u001b[49m | ||
\u001b[100m \u001b[49m | ||
\u001b[100m \u001b[49m | ||
\u001b[100m \u001b[49m | ||
\u001b[46m \u001b[49m | ||
\u001b[46m \u001b[49m | ||
\u001b[100m \u001b[49m | ||
\u001b[100m \u001b[49m | ||
\u001b[100m \u001b[49m | ||
\u001b[100m \u001b[49m" | ||
`) | ||
}) | ||
|
||
test('renders correctly when at the bottom of the list', async () => { | ||
const options = { | ||
containerHeight: 10, | ||
visibleListSectionLength: 10, | ||
fullListLength: 50, | ||
visibleFromIndex: 40, | ||
} | ||
|
||
const {lastFrame} = render(<Scrollbar {...options} />) | ||
|
||
// Last 2 are colored in | ||
expect(lastFrame()).toMatchInlineSnapshot(` | ||
"\u001b[100m \u001b[49m | ||
\u001b[100m \u001b[49m | ||
\u001b[100m \u001b[49m | ||
\u001b[100m \u001b[49m | ||
\u001b[100m \u001b[49m | ||
\u001b[100m \u001b[49m | ||
\u001b[100m \u001b[49m | ||
\u001b[100m \u001b[49m | ||
\u001b[46m \u001b[49m | ||
\u001b[46m \u001b[49m" | ||
`) | ||
}) | ||
|
||
test('renders correctly in the middle of the list in no-color mode', async () => { | ||
const options = { | ||
containerHeight: 10, | ||
visibleListSectionLength: 10, | ||
fullListLength: 50, | ||
visibleFromIndex: 20, | ||
noColor: true, | ||
} | ||
|
||
const {lastFrame} = render(<Scrollbar {...options} />) | ||
// Scrollbar is in the middle | ||
expect(lastFrame()).toMatchInlineSnapshot(` | ||
"△ | ||
│ | ||
│ | ||
│ | ||
║ | ||
║ | ||
│ | ||
│ | ||
│ | ||
▽" | ||
`) | ||
}) | ||
}) |
79 changes: 79 additions & 0 deletions
79
packages/cli-kit/src/private/node/ui/components/Scrollbar.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import {shouldDisplayColors} from '../../../../public/node/output.js' | ||
import {Box, Text} from 'ink' | ||
import React, {FunctionComponent} from 'react' | ||
|
||
export interface ScrollbarProps { | ||
containerHeight: number | ||
visibleListSectionLength: number | ||
fullListLength: number | ||
visibleFromIndex: number | ||
noColor?: boolean | ||
} | ||
|
||
const BACKGROUND_CHAR = '│' | ||
const SCROLLBOX_CHAR = '║' | ||
|
||
const Scrollbar: FunctionComponent<ScrollbarProps> = ({ | ||
containerHeight, | ||
visibleListSectionLength, | ||
fullListLength, | ||
visibleFromIndex, | ||
noColor = !shouldDisplayColors(), | ||
}) => { | ||
const displayArrows = containerHeight >= 4 && noColor | ||
const visibleToIndex = visibleFromIndex + visibleListSectionLength - 1 | ||
|
||
// Leave 2 rows for top/bottom arrows when there is vertical room for them. | ||
const fullHeight = displayArrows ? containerHeight - 2 : containerHeight | ||
const scrollboxHeight = Math.min( | ||
fullHeight - 1, | ||
Math.ceil(Math.min(1, visibleListSectionLength / fullListLength) * fullHeight), | ||
) | ||
|
||
let topBuffer: number | ||
// Ensure it scrolls all the way to the bottom when we hit the bottom | ||
if (visibleToIndex >= fullListLength - 1) { | ||
topBuffer = fullHeight - scrollboxHeight | ||
} else { | ||
// This is the actual number of rows available for the scrollbar to go up and down | ||
const scrollingLength = fullHeight - scrollboxHeight | ||
// This is the number of times the screen itself can scroll down | ||
const scrollableIncrements = fullListLength - visibleListSectionLength | ||
|
||
topBuffer = Math.max( | ||
// Never go negative, that causes errors! | ||
0, | ||
Math.min( | ||
// Never have more buffer than filling in all spaces above the scrollbox | ||
fullHeight - scrollboxHeight, | ||
Math.round((visibleFromIndex / scrollableIncrements) * scrollingLength), | ||
), | ||
) | ||
} | ||
const bottomBuffer = fullHeight - scrollboxHeight - topBuffer | ||
|
||
const backgroundChar = noColor ? BACKGROUND_CHAR : ' ' | ||
const scrollboxChar = noColor ? SCROLLBOX_CHAR : ' ' | ||
const bgColor = noColor ? undefined : 'gray' | ||
const scrollboxColor = noColor ? undefined : 'cyan' | ||
|
||
return ( | ||
<Box flexDirection="column"> | ||
{displayArrows ? <Text>△</Text> : null} | ||
|
||
<Box width={1}> | ||
<Text backgroundColor={bgColor}>{backgroundChar.repeat(topBuffer)}</Text> | ||
</Box> | ||
<Box width={1}> | ||
<Text backgroundColor={scrollboxColor}>{scrollboxChar.repeat(scrollboxHeight)}</Text> | ||
</Box> | ||
<Box width={1}> | ||
<Text backgroundColor={bgColor}>{backgroundChar.repeat(bottomBuffer)}</Text> | ||
</Box> | ||
|
||
{displayArrows ? <Text>▽</Text> : null} | ||
</Box> | ||
) | ||
} | ||
|
||
export {Scrollbar} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.