1 | <h1 align="center">
|
2 | <br>
|
3 | <br>
|
4 | <img width="300" alt="Ink" src="media/logo.png">
|
5 | <br>
|
6 | <br>
|
7 | <br>
|
8 | </h1>
|
9 |
|
10 | > React for CLIs. Build and test your CLI output using components.
|
11 |
|
12 | [![Build Status](https://travis-ci.org/vadimdemedes/ink.svg?branch=master)](https://travis-ci.org/vadimdemedes/ink)
|
13 |
|
14 |
|
15 | ## Install
|
16 |
|
17 | ```
|
18 | $ npm install ink react
|
19 | ```
|
20 |
|
21 |
|
22 | ## Usage
|
23 |
|
24 | ```jsx
|
25 | import React, {Component} from 'react';
|
26 | import {render, Color} from 'ink';
|
27 |
|
28 | class Counter extends Component {
|
29 | constructor() {
|
30 | super();
|
31 |
|
32 | this.state = {
|
33 | i: 0
|
34 | };
|
35 | }
|
36 |
|
37 | render() {
|
38 | return (
|
39 | <Color green>
|
40 | {this.state.i} tests passed
|
41 | </Color>
|
42 | );
|
43 | }
|
44 |
|
45 | componentDidMount() {
|
46 | this.timer = setInterval(() => {
|
47 | this.setState({
|
48 | i: this.state.i + 1
|
49 | });
|
50 | }, 100);
|
51 | }
|
52 |
|
53 | componentWillUnmount() {
|
54 | clearInterval(this.timer);
|
55 | }
|
56 | }
|
57 |
|
58 | render(<Counter/>);
|
59 | ```
|
60 |
|
61 | <img src="media/demo.svg" width="600">
|
62 |
|
63 | You can also check it out live on [repl.it sandbox](https://ink-counter-demo.vadimdemedes.repl.run/).
|
64 | Feel free to play around with the code and fork this repl at [https://repl.it/@vadimdemedes/ink-counter-demo](https://repl.it/@vadimdemedes/ink-counter-demo).
|
65 |
|
66 |
|
67 | ## Built with Ink
|
68 |
|
69 | - [emoj](https://github.com/sindresorhus/emoj) - Find relevant emoji on the command-line.
|
70 | - [emma](https://github.com/maticzav/emma-cli) - Terminal assistant to find and install npm packages.
|
71 | - [swiff](https://github.com/simple-integrated-marketing/swiff) - Multi-environment command line tools for time-saving web developers.
|
72 | - [changelog-view](https://github.com/jdeniau/changelog-view) - Tool view changelog in console.
|
73 |
|
74 |
|
75 | ## Contents
|
76 |
|
77 | - [Getting Started](#getting-started)
|
78 | - [Examples](#examples)
|
79 | - [API](#api)
|
80 | - [Building Layouts](#building-layouts)
|
81 | - [Built-in Components](#built-in-components)
|
82 | - [Useful Components](#useful-components)
|
83 | - [Testing](#testing)
|
84 |
|
85 |
|
86 | ## Getting Started
|
87 |
|
88 | Ink's goal is to provide the same component-based UI building experience that React provides, but for command-line apps. It uses [yoga-layout](https://github.com/facebook/yoga) to allow Flexbox layouts in the terminal. If you are already familiar with React, you already know Ink.
|
89 |
|
90 | The key difference you have to remember is that the rendering result isn't a DOM, but a string, which Ink writes to the output.
|
91 |
|
92 | To ensure all examples work and you can begin your adventure with Ink, make sure to set up Babel with a React preset. After [installing Babel](https://babeljs.io/docs/en/usage), configure it in `package.json`:
|
93 |
|
94 | ```json
|
95 | {
|
96 | "babel": {
|
97 | "presets": [
|
98 | "@babel/preset-react"
|
99 | ]
|
100 | }
|
101 | }
|
102 | ```
|
103 |
|
104 | Don't forget to import `React` into every file that contains JSX:
|
105 |
|
106 | ```jsx
|
107 | import React from 'react';
|
108 | import {render, Box} from 'ink';
|
109 |
|
110 | const Demo = () => (
|
111 | <Box>
|
112 | Hello World
|
113 | </Box>
|
114 | );
|
115 |
|
116 | render(<Demo/>);
|
117 | ```
|
118 |
|
119 |
|
120 | ## Examples
|
121 |
|
122 | - [Jest](examples/jest/jest.js) - Implementation of basic Jest UI [(live demo)](https://ink-jest-demo.vadimdemedes.repl.run/).
|
123 | - [Counter](examples/counter/counter.js) - Simple counter that increments every 100ms [(live demo)](https://ink-counter-demo.vadimdemedes.repl.run/).
|
124 |
|
125 |
|
126 | ## API
|
127 |
|
128 | Since Ink is a React renderer, it means that all features of React are supported.
|
129 | Head over to [React](https://reactjs.org) website for documentation on how to use it.
|
130 | In this readme only Ink's methods will be documented.
|
131 |
|
132 | #### render(tree, options)
|
133 |
|
134 | Returns: `Instance`
|
135 |
|
136 | Mount a component and render the output.
|
137 |
|
138 | ##### tree
|
139 |
|
140 | Type: `ReactElement`
|
141 |
|
142 | ##### options
|
143 |
|
144 | Type: `Object`
|
145 |
|
146 | ###### stdout
|
147 |
|
148 | Type: `stream.Writable`<br>
|
149 | Default: `process.stdout`
|
150 |
|
151 | Output stream where app will be rendered.
|
152 |
|
153 | ###### stdin
|
154 |
|
155 | Type: `stream.Readable`<br>
|
156 | Default: `process.stdin`
|
157 |
|
158 | Input stream where app will listen for input.
|
159 |
|
160 | ###### exitOnCtrlC
|
161 |
|
162 | Type: `boolean`<br>
|
163 | Default: `true`
|
164 |
|
165 | Configure whether Ink should listen to Ctrl+C keyboard input and exit the app.
|
166 | This is needed in case `process.stdin` is in [raw mode](https://nodejs.org/api/tty.html#tty_readstream_setrawmode_mode), because then Ctrl+C is ignored by default and process is expected to handle it manually.
|
167 |
|
168 | ###### debug
|
169 |
|
170 | Type: `boolean`<br>
|
171 | Default: `false`
|
172 |
|
173 | If `true`, each update will be rendered as a separate output, without replacing the previous one.
|
174 |
|
175 | ```jsx
|
176 | import React, {Component} from 'react';
|
177 | import {render, Box} from 'ink';
|
178 |
|
179 | class Counter extends Component {
|
180 | constructor() {
|
181 | super();
|
182 |
|
183 | this.state = {
|
184 | i: 0
|
185 | };
|
186 | }
|
187 |
|
188 | render() {
|
189 | return (
|
190 | <Box>
|
191 | Iteration #{this.state.i}
|
192 | </Box>
|
193 | );
|
194 | }
|
195 |
|
196 | componentDidMount() {
|
197 | this.timer = setInterval(() => {
|
198 | this.setState(prevState => ({
|
199 | i: prevState.i + 1
|
200 | }));
|
201 | }, 100);
|
202 | }
|
203 |
|
204 | componentWillUnmount() {
|
205 | clearInterval(this.timer);
|
206 | }
|
207 | }
|
208 |
|
209 | const app = render(<Counter/>);
|
210 |
|
211 | setTimeout(() => {
|
212 | // Enough counting
|
213 | app.unmount();
|
214 | }, 1000);
|
215 | ```
|
216 |
|
217 | There's also a shortcut to avoid passing `options` object:
|
218 |
|
219 | ```jsx
|
220 | render(<Counter>, process.stdout);
|
221 | ```
|
222 |
|
223 | #### Instance
|
224 |
|
225 | This is the object that `render()` returns.
|
226 |
|
227 | ##### rerender
|
228 |
|
229 | Replace previous root node with a new one or update props of the current root node.
|
230 |
|
231 | ```jsx
|
232 | // Update props of the root node
|
233 | const {rerender} = render(<Counter count={1}/>);
|
234 | rerender(<Counter count={2}/>);
|
235 |
|
236 | // Replace root node
|
237 | const {rerender} = render(<OldCounter/>);
|
238 | rerender(<NewCounter/>);
|
239 | ```
|
240 |
|
241 | ##### unmount
|
242 |
|
243 | Manually unmount the whole Ink app.
|
244 |
|
245 | ```jsx
|
246 | const {unmount} = render(<MyApp/>);
|
247 | unmount();
|
248 | ```
|
249 |
|
250 | ##### waitUntilExit
|
251 |
|
252 | Returns a promise, which resolves when app is unmounted.
|
253 |
|
254 | ```jsx
|
255 | const {unmount, waitUntilExit} = render(<MyApp/>);
|
256 |
|
257 | setTimeout(unmount, 1000);
|
258 |
|
259 | await waitUntilExit(); // resolves after `unmount()` is called
|
260 | ```
|
261 |
|
262 | ## Building Layouts
|
263 |
|
264 | Ink uses [Yoga](https://github.com/facebook/yoga) - a Flexbox layout engine to build great user interfaces for your CLIs.
|
265 | It's important to remember that each element is a Flexbox container.
|
266 | Think of it as if each `<div>` in the browser had `display: flex`.
|
267 | See `<Box>` built-in component below for documentation on how to use Flexbox layouts in Ink.
|
268 |
|
269 |
|
270 | ### Built-in Components
|
271 |
|
272 | #### `<Box>`
|
273 |
|
274 | `<Box>` it's an essential Ink component to build your layout. It's like a `<div style="display: flex">` in a browser.
|
275 |
|
276 | Import:
|
277 |
|
278 | ```js
|
279 | import {Box} from 'ink';
|
280 | ```
|
281 |
|
282 | ##### Padding
|
283 |
|
284 | ###### paddingTop
|
285 |
|
286 | Type: `number`<br>
|
287 | Default: `0`
|
288 |
|
289 | ###### paddingBottom
|
290 |
|
291 | Type: `number`<br>
|
292 | Default: `0`
|
293 |
|
294 | ###### paddingLeft
|
295 |
|
296 | Type: `number`<br>
|
297 | Default: `0`
|
298 |
|
299 | ###### paddingRight
|
300 |
|
301 | Type: `number`<br>
|
302 | Default: `0`
|
303 |
|
304 | ###### paddingX
|
305 |
|
306 | Type: `number`<br>
|
307 | Default: `0`
|
308 |
|
309 | ###### paddingY
|
310 |
|
311 | Type: `number`<br>
|
312 | Default: `0`
|
313 |
|
314 | ###### padding
|
315 |
|
316 | Type: `number`<br>
|
317 | Default: `0`
|
318 |
|
319 | ```jsx
|
320 | <Box paddingTop={2}>Top</Box>
|
321 | <Box paddingBottom={2}>Bottom</Box>
|
322 | <Box paddingLeft={2}>Left</Box>
|
323 | <Box paddingRight={2}>Right</Box>
|
324 | <Box paddingX={2}>Left and right</Box>
|
325 | <Box paddingY={2}>Top and bottom</Box>
|
326 | <Box padding={2}>Top, bottom, left and right</Box>
|
327 | ```
|
328 |
|
329 | ##### Margin
|
330 |
|
331 | ###### marginTop
|
332 |
|
333 | Type: `number`<br>
|
334 | Default: `0`
|
335 |
|
336 | ###### marginBottom
|
337 |
|
338 | Type: `number`<br>
|
339 | Default: `0`
|
340 |
|
341 | ###### marginLeft
|
342 |
|
343 | Type: `number`<br>
|
344 | Default: `0`
|
345 |
|
346 | ###### marginRight
|
347 |
|
348 | Type: `number`<br>
|
349 | Default: `0`
|
350 |
|
351 | ###### marginX
|
352 |
|
353 | Type: `number`<br>
|
354 | Default: `0`
|
355 |
|
356 | ###### marginY
|
357 |
|
358 | Type: `number`<br>
|
359 | Default: `0`
|
360 |
|
361 | ###### margin
|
362 |
|
363 | Type: `number`<br>
|
364 | Default: `0`
|
365 |
|
366 | ```jsx
|
367 | <Box marginTop={2}>Top</Box>
|
368 | <Box marginBottom={2}>Bottom</Box>
|
369 | <Box marginLeft={2}>Left</Box>
|
370 | <Box marginRight={2}>Right</Box>
|
371 | <Box marginX={2}>Left and right</Box>
|
372 | <Box marginY={2}>Top and bottom</Box>
|
373 | <Box margin={2}>Top, bottom, left and right</Box>
|
374 | ```
|
375 |
|
376 | ##### Flex
|
377 |
|
378 | ###### flexGrow
|
379 |
|
380 | Type: `number`<br>
|
381 | Default: `0`
|
382 |
|
383 | See [flex-grow](https://css-tricks.com/almanac/properties/f/flex-grow/).
|
384 |
|
385 | ```jsx
|
386 | <Box>
|
387 | Label:
|
388 | <Box flexGrow={1}>
|
389 | Fills all remaining space
|
390 | </Box>
|
391 | </Box>
|
392 | ```
|
393 |
|
394 | ###### flexShrink
|
395 |
|
396 | Type: `number`<br>
|
397 | Default: `1`
|
398 |
|
399 | See [flex-shrink](https://css-tricks.com/almanac/properties/f/flex-shrink/).
|
400 |
|
401 | ```jsx
|
402 | <Box width={20}>
|
403 | <Box flexShrink={2} width={10}>
|
404 | Will be 1/4
|
405 | </Box>
|
406 | <Box width={10}>
|
407 | Will be 3/4
|
408 | </Box>
|
409 | </Box>
|
410 | ```
|
411 |
|
412 | ###### flexDirection
|
413 |
|
414 | Type: `string`<br>
|
415 | Allowed values: `row`, `row-reverse`, `column` and `column-reverse`
|
416 |
|
417 | See [flex-direction](https://css-tricks.com/almanac/properties/f/flex-direction/).
|
418 |
|
419 | ```jsx
|
420 | <Box>
|
421 | <Box marginRight={1}>X</Box>
|
422 | <Box>Y</Box>
|
423 | </Box>
|
424 | // X Y
|
425 |
|
426 | <Box flexDirection="row-reverse">
|
427 | <Box>X</Box>
|
428 | <Box marginRight={1}>Y</Box>
|
429 | </Box>
|
430 | // Y X
|
431 |
|
432 | <Box flexDirection="column">
|
433 | <Box>X</Box>
|
434 | <Box>Y</Box>
|
435 | </Box>
|
436 | // X
|
437 | // Y
|
438 |
|
439 | <Box flexDirection="column-reverse">
|
440 | <Box>X</Box>
|
441 | <Box>Y</Box>
|
442 | </Box>
|
443 | // Y
|
444 | // X
|
445 | ```
|
446 |
|
447 | ###### alignItems
|
448 |
|
449 | Type: `string`<br>
|
450 | Allowed values: `flex-start`, `center` and `flex-end`
|
451 |
|
452 | See [align-items](https://css-tricks.com/almanac/properties/f/align-items/).
|
453 |
|
454 | ```jsx
|
455 | <Box alignItems="flex-start">
|
456 | <Box marginRight={1}>X</Box>
|
457 | <Box>{`A\nB\nC`}</Box>
|
458 | </Box>
|
459 | // X A
|
460 | // B
|
461 | // C
|
462 |
|
463 | <Box alignItems="center">
|
464 | <Box marginRight={1}>X</Box>
|
465 | <Box>{`A\nB\nC`}</Box>
|
466 | </Box>
|
467 | // A
|
468 | // X B
|
469 | // C
|
470 |
|
471 | <Box alignItems="flex-end">
|
472 | <Box marginRight={1}>X</Box>
|
473 | <Box>{`A\nB\nC`}</Box>
|
474 | </Box>
|
475 | // A
|
476 | // B
|
477 | // X C
|
478 | ```
|
479 |
|
480 | ###### justifyContent
|
481 |
|
482 | Type: `string`<br>
|
483 | Allowed values: `flex-start`, `center`, `flex-end`, `space-between` and `space-around`.
|
484 |
|
485 | See [justify-content](https://css-tricks.com/almanac/properties/f/justify-content/).
|
486 |
|
487 | ```jsx
|
488 | <Box justifyContent="flex-start">
|
489 | <Box>X</Box>
|
490 | </Box>
|
491 | // [X ]
|
492 |
|
493 | <Box justifyContent="center">
|
494 | <Box>X</Box>
|
495 | </Box>
|
496 | // [ X ]
|
497 |
|
498 | <Box justifyContent="flex-end">
|
499 | <Box>X</Box>
|
500 | </Box>
|
501 | // [ X]
|
502 |
|
503 | <Box justifyContent="space-between">
|
504 | <Box>X</Box>
|
505 | <Box>Y</Box>
|
506 | </Box>
|
507 | // [X Y]
|
508 |
|
509 | <Box justifyContent="space-around">
|
510 | <Box>X</Box>
|
511 | <Box>Y</Box>
|
512 | </Box>
|
513 | // [ X Y ]
|
514 | ```
|
515 |
|
516 |
|
517 | #### `<Color>`
|
518 |
|
519 | The `<Color>` compoment is a simple wrapper around [the `chalk` API](https://github.com/chalk/chalk#api).
|
520 | It supports all of the chalk's methods as `props`.
|
521 |
|
522 | Import:
|
523 |
|
524 | ```js
|
525 | import {Color} from 'ink';
|
526 | ```
|
527 |
|
528 | Usage:
|
529 |
|
530 | ```jsx
|
531 | <Color rgb={[255, 255, 255]} bgKeyword="magenta">
|
532 | Hello!
|
533 | </Color>
|
534 |
|
535 | <Color hex="#000000" bgHex="#FFFFFF">
|
536 | Hey there
|
537 | </Color>
|
538 |
|
539 | <Color blue>
|
540 | I'm blue
|
541 | </Color>
|
542 | ```
|
543 |
|
544 | #### `<Text>`
|
545 |
|
546 | This component can change the style of the text, make it bold, underline, italic or strikethrough.
|
547 |
|
548 | Import:
|
549 |
|
550 | ```js
|
551 | import {Text} from 'ink';
|
552 | ```
|
553 |
|
554 | ##### bold
|
555 |
|
556 | Type: `boolean`<br>
|
557 | Default: `false`
|
558 |
|
559 | ##### italic
|
560 |
|
561 | Type: `boolean`<br>
|
562 | Default: `false`
|
563 |
|
564 | ##### underline
|
565 |
|
566 | Type: `boolean`<br>
|
567 | Default: `false`
|
568 |
|
569 | ##### strikethrough
|
570 |
|
571 | Type: `boolean`<br>
|
572 | Default: `false`
|
573 |
|
574 | Usage:
|
575 |
|
576 | ```jsx
|
577 | <Text bold>I am bold</Text>
|
578 | <Text italic>I am italic</Text>
|
579 | <Text underline>I am underline</Text>
|
580 | <Text strikethrough>I am strikethrough</Text>
|
581 | ```
|
582 |
|
583 | #### `<Static>`
|
584 |
|
585 | `<Static>` component allows permanently rendering output to stdout and preserving it across renders.
|
586 | Components passed to `<Static>` as children will be written to stdout only once and will never be rerendered.
|
587 | `<Static>` output comes first, before any other output from your components, no matter where it is in the tree.
|
588 | In order for this mechanism to work properly, at most one `<Static>` component must be present in your node tree and components that were rendered must never update their output. Ink will detect new children appended to `<Static>` and render them to stdout.
|
589 |
|
590 | **Note:** `<Static>` accepts only an array of children and each of them must have a unique key.
|
591 |
|
592 | Example use case for this component is Jest's output:
|
593 |
|
594 | ![](https://jestjs.io/img/content/feature-fast.png)
|
595 |
|
596 | Jest continuously writes the list of completed tests to the output, while updating test results at the bottom of the output in real-time. Here's how this user interface could be implemented with Ink:
|
597 |
|
598 | ```jsx
|
599 | <>
|
600 | <Static>
|
601 | {tests.map(test => (
|
602 | <Test key={test.id} title={test.title}/>
|
603 | ))}
|
604 | </Static>
|
605 |
|
606 | <Box marginTop={1}>
|
607 | <TestResults passed={results.passed} failed={results.failed}/>
|
608 | </Box>
|
609 | </>
|
610 | ```
|
611 |
|
612 | See [examples/jest](examples/jest/jest.js) for a basic implementation of Jest's UI.
|
613 |
|
614 | #### `<AppContext>`
|
615 |
|
616 | `<AppContext>` is a [React context](https://reactjs.org/docs/context.html#reactcreatecontext), which exposes a method to manually exit the app (unmount).
|
617 |
|
618 | Import:
|
619 |
|
620 | ```js
|
621 | import {AppContext} from 'ink';
|
622 | ```
|
623 |
|
624 | ##### exit
|
625 |
|
626 | Type: `Function`
|
627 |
|
628 | Exit (unmount) the whole Ink app.
|
629 |
|
630 | Usage:
|
631 |
|
632 | ```jsx
|
633 | <AppContext.Consumer>
|
634 | {({ exit }) => (
|
635 | {/* Calling `onExit()` from within <MyApp> will unmount the app */}
|
636 | <MyApp onExit={exit}/>
|
637 | )}
|
638 | </AppContext.Consumer>
|
639 | ```
|
640 |
|
641 | #### `<StdinContext>`
|
642 |
|
643 | `<StdinContext>` is a [React context](https://reactjs.org/docs/context.html#reactcreatecontext), which exposes input stream.
|
644 |
|
645 | Import:
|
646 |
|
647 | ```js
|
648 | import {StdinContext} from 'ink';
|
649 | ```
|
650 |
|
651 | ##### stdin
|
652 |
|
653 | Type: `stream.Readable`<br>
|
654 | Default: `process.stdin`
|
655 |
|
656 | Stdin stream passed to `render()` in `options.stdin` or `process.stdin` by default.
|
657 | Useful if your app needs to handle user input.
|
658 |
|
659 | Usage:
|
660 |
|
661 | ```jsx
|
662 | <StdinContext.Consumer>
|
663 | {({ stdin }) => (
|
664 | <MyComponent stdin={stdin}/>
|
665 | )}
|
666 | </StdinContext.Consumer>
|
667 | ```
|
668 |
|
669 | ##### setRawMode
|
670 |
|
671 | Type: `function`<br>
|
672 |
|
673 | See [`setRawMode`](https://nodejs.org/api/tty.html#tty_readstream_setrawmode_mode).
|
674 | Ink exposes this function via own `<StdinContext>` to be able to handle <kbd>Ctrl</kbd>+<kbd>C</kbd>, that's why you should use Ink's `setRawMode` instead of `process.stdin.setRawMode`. Ink also enables `keypress` events via [`readline.emitKeypressEvents()`](https://nodejs.org/api/readline.html#readline_readline_emitkeypressevents_stream_interface) when raw mode is enabled.
|
675 |
|
676 | Usage:
|
677 |
|
678 | ```jsx
|
679 | <StdinContext.Consumer>
|
680 | {({ setRawMode }) => (
|
681 | <MyComponent setRawMode={setRawMode}/>
|
682 | )}
|
683 | </StdinContext.Consumer>
|
684 | ```
|
685 |
|
686 | #### `<StdoutContext>`
|
687 |
|
688 | `<StdoutContext>` is a [React context](https://reactjs.org/docs/context.html#reactcreatecontext), which exposes stdout stream, where Ink renders your app.
|
689 |
|
690 | Import:
|
691 |
|
692 | ```js
|
693 | import {StdoutContext} from 'ink';
|
694 | ```
|
695 |
|
696 | ##### stdout
|
697 |
|
698 | Type: `stream.Writable`<br>
|
699 | Default: `process.stdout`
|
700 |
|
701 | Usage:
|
702 |
|
703 | ```jsx
|
704 | <StdoutContext.Consumer>
|
705 | {({ stdout }) => (
|
706 | <MyComponent stdout={stdout}/>
|
707 | )}
|
708 | </StdoutContext.Consumer>
|
709 | ```
|
710 |
|
711 |
|
712 | ## Useful Components
|
713 |
|
714 | - [ink-text-input](https://github.com/vadimdemedes/ink-text-input) - Text input.
|
715 | - [ink-spinner](https://github.com/vadimdemedes/ink-spinner) - Spinner.
|
716 | - [ink-select-input](https://github.com/vadimdemedes/ink-select-input) - Select (dropdown) input.
|
717 | - [ink-link](https://github.com/sindresorhus/ink-link) - Link component.
|
718 | - [ink-box](https://github.com/sindresorhus/ink-box) - Styled box component.
|
719 | - [ink-gradient](https://github.com/sindresorhus/ink-gradient) - Gradient color component.
|
720 | - [ink-big-text](https://github.com/sindresorhus/ink-big-text) - Awesome text component.
|
721 | - [ink-image](https://github.com/kevva/ink-image) - Display images inside the terminal.
|
722 | - [ink-tab](https://github.com/jdeniau/ink-tab) - Tab component.
|
723 |
|
724 | ### Incompatible components
|
725 |
|
726 | These are components that haven't migrated to Ink 2 yet:
|
727 |
|
728 | - [ink-progress-bar](https://github.com/brigand/ink-progress-bar) - Configurable component for rendering progress bars.
|
729 | - [ink-console](https://github.com/ForbesLindesay/ink-console) - Render output from `console[method]` calls in a scrollable panel.
|
730 | - [ink-confirm-input](https://github.com/kevva/ink-confirm-input) - Yes/No confirmation input.
|
731 | - [ink-checkbox-list](https://github.com/MaxMEllon/ink-checkbox-list) - Checkbox.
|
732 | - [ink-quicksearch](https://github.com/aicioara/ink-quicksearch) - Select Component with fast quicksearch-like navigation
|
733 | - [ink-autocomplete](https://github.com/maticzav/ink-autocomplete) - Autocomplete.
|
734 | - [ink-table](https://github.com/maticzav/ink-table) - Table component.
|
735 | - [ink-broadcast](https://github.com/jimmed/ink-broadcast) - Implementation of react-broadcast for Ink.
|
736 | - [ink-router](https://github.com/jimmed/ink-router) - Implementation of react-router for Ink.
|
737 | - [ink-select](https://github.com/karaggeorge/ink-select) - Select component.
|
738 | - [ink-scrollbar](https://github.com/karaggeorge/ink-scrollbar) - Scrollbar component.
|
739 | - [ink-text-animation](https://github.com/yardnsm/ink-text-animation) - Text animation component.
|
740 | - [ink-figlet](https://github.com/KimotoYanke/ink-figlet) - Large text component with Figlet fonts.
|
741 | - [ink-divider](https://github.com/JureSotosek/ink-divider) - A divider component.
|
742 |
|
743 |
|
744 | ## Testing
|
745 |
|
746 | Ink components are simple to test with [ink-testing-library](https://github.com/vadimdemedes/ink-testing-library).
|
747 | Here's a simple example that checks how component is rendered:
|
748 |
|
749 | ```jsx
|
750 | import React from 'react';
|
751 | import {Text} from 'ink';
|
752 | import {render} from 'ink-testing-library';
|
753 |
|
754 | const Test = () => <Text>Hello World</Text>;
|
755 | const {lastFrame} = render(<Test/>);
|
756 |
|
757 | lastFrame() === 'Hello World'; //=> true
|
758 | ```
|
759 |
|
760 | Visit [ink-testing-library](https://github.com/vadimdemedes/ink-testing-library) for more examples and full documentation.
|
761 |
|
762 |
|
763 | ## Maintainers
|
764 |
|
765 | - [Vadim Demedes](https://github.com/vadimdemedes)
|
766 | - [Sindre Sorhus](https://github.com/sindresorhus)
|
767 |
|
768 |
|
769 | ## License
|
770 |
|
771 | MIT
|