Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: vadimdemedes/ink-select-input
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v3.1.0
Choose a base ref
...
head repository: vadimdemedes/ink-select-input
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v3.1.1
Choose a head ref
  • 4 commits
  • 6 files changed
  • 4 contributors

Commits on Mar 28, 2019

  1. Add TS typings (#17)

    * Add TS typings
    
    * Add docs
    
    * Add typechecking script to tests
    
    * style updates
    
    * Add typings for custom indicator and item component
    
    * update formating
    
    * Update index.d.ts
    sebald authored and Vadim Demedes committed Mar 28, 2019
    Copy the full SHA
    45def68 View commit details
  2. Add support for optional onHighlight handler (#16)

    * Add support for optional onChange handler
    
    * Update onChange test to account for limit
    
    * Rename onChange to onHighlight
    
    * Refactor onHighlight code to not use componentDidUpdate
    
    Using componentDidUpdate was making testing unnecessarily hard. This solution a bit of a "code smell" in my eyes, but is more easily tested
    
    * Update readme.md
    
    Add onHighlight section
    zimme authored and Vadim Demedes committed Mar 28, 2019

    Unverified

    This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
    Copy the full SHA
    59f3ee3 View commit details

Commits on May 5, 2019

  1. Add type definition for onHighlight prop (#19)

    cblanc authored and Vadim Demedes committed May 5, 2019
    Copy the full SHA
    6e0f6f4 View commit details
  2. 3.1.1

    Vadim Demedes committed May 5, 2019
    Copy the full SHA
    d9b382b View commit details
Showing with 167 additions and 11 deletions.
  1. +75 −0 index.d.ts
  2. +36 −0 index.test-d.tsx
  3. +8 −4 package.json
  4. +6 −0 readme.md
  5. +19 −7 src/SelectInput.js
  6. +23 −0 test.js
75 changes: 75 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import * as React from 'react';

/**
* Props for custom indicator component.
*/
export type IndicatorProps = { isSelected?: boolean };

/**
* Props for custom item component.
*/
export type ItemProps = {
isSelected?: boolean;
label: string;
};

/**
* Select item definition.
*/
export type Item = {
label: string;
value: React.Key;
key?: React.Key;
};

export type InkSelectInputProps = {
/**
* Items to display in a list. Each item must be an object and have `label` and `value` props,
* it may also optionally have a `key` prop.
* If no `key` prop is provided, `value` will be used as the item key.
*/
items?: Item[];

/**
* Listen to user's input. Useful in case there are multiple input components
* at the same time and input must be "routed" to a specific component.
*/
focus?: boolean;

/**
* Index of initially-selected item in `items` array.
*/
initialIndex?: number;

/**
* Function to call when user selects an item.
* Item object is passed to that function as an argument.
*/
onSelect?: (item: Item) => void;

/**
* Function to call when user highlights an item.
* Item object is passed to that function as an argument.
*/
onHighlight?: (item: Item) => void;

/**
* Custom component to override the default indicator component.
*/
indicatorComponent?: React.ComponentType<IndicatorProps>;

/**
* Custom component to override the default item component.
*/
itemComponent?: React.ComponentType<ItemProps>;

/**
* Number of items to display.
*/
limit?: number;
};

/**
* Select input component for Ink
*/
export default class InkSelectInput extends React.Component<InkSelectInputProps> {}
36 changes: 36 additions & 0 deletions index.test-d.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import * as React from 'react';
import InkSelect, {Item, IndicatorProps, ItemProps} from '.';

const items: Item[] = [
{label: 'label 1', value: 'label 1'},
{label: 'label 2', value: 'label 2'}
];
const keyedItems: Item[] = [
{label: 'label 1', value: 'label 1', key: 1},
{label: 'label 2', value: 'label 2', key: 2}
];

const plain = () => <InkSelect />;
const withProps = () => <InkSelect focus={false} limit={10} />;

const selectWithItems = () => <InkSelect items={items} />;
const selectWithKeyedItems = () => <InkSelect items={keyedItems} />;

const onSelect = (item: Item) => console.log(item);
const onHighlight = (item: Item) => console.log(item);
const withHandler = () => <InkSelect onSelect={onSelect} onHighlight={onHighlight} />;

const CustomIndikator: React.FC<IndicatorProps> = ({isSelected}) => (
<div>{isSelected ? '✓' : ''}</div>
);
const CustomItemComponent: React.FC<ItemProps> = ({isSelected, label}) => (
<div>
{isSelected ? '✓' : ''} {label}
</div>
);
const overrideComponents = () => (
<InkSelect
indicatorComponent={CustomIndikator}
itemComponent={CustomItemComponent}
/>
);
12 changes: 8 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ink-select-input",
"version": "3.1.0",
"version": "3.1.1",
"description": "Select input component for Ink",
"license": "MIT",
"repository": "vadimdemedes/ink-select-input",
@@ -15,12 +15,14 @@
},
"scripts": {
"pretest": "npm run build",
"test": "xo && ava",
"test": "xo && ava && npm run typecheck",
"build": "babel src --out-dir=build",
"prepare": "npm run build"
"prepare": "npm run build",
"typecheck": "tsc --noEmit --jsx react index.test-d.tsx"
},
"files": [
"build"
"build",
"index.d.ts"
],
"keywords": [
"ink",
@@ -37,6 +39,7 @@
"@babel/core": "^7.3.3",
"@babel/plugin-proposal-class-properties": "^7.3.3",
"@babel/preset-react": "^7.0.0",
"@types/react": "^16.8.8",
"ava": "*",
"babel-eslint": "^10.0.1",
"eslint-config-xo-react": "^0.16.0",
@@ -45,6 +48,7 @@
"ink-testing-library": "^1.0.0",
"react": "^16.8.2",
"sinon": "^7.2.7",
"typescript": "^3.3.3333",
"xo": "*"
},
"babel": {
6 changes: 6 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -72,6 +72,12 @@ Type: `function`

Function to call when user selects an item. Item object is passed to that function as an argument.

### onHighlight

Type: `function`

Function to call when user highlights an item. Item object is passed to that function as an argument.

### indicatorComponent

Type: `Component`
26 changes: 19 additions & 7 deletions src/SelectInput.js
Original file line number Diff line number Diff line change
@@ -18,7 +18,8 @@ class SelectInput extends PureComponent {
indicatorComponent: PropTypes.func,
itemComponent: PropTypes.func,
limit: PropTypes.number,
onSelect: PropTypes.func
onSelect: PropTypes.func,
onHighlight: PropTypes.func
}

static defaultProps = {
@@ -28,7 +29,8 @@ class SelectInput extends PureComponent {
indicatorComponent: Indicator,
itemComponent: Item,
limit: null,
onSelect() {}
onSelect() {},
onHighlight() {}
}

state = {
@@ -83,7 +85,7 @@ class SelectInput extends PureComponent {
}

handleInput = data => {
const {items, focus, onSelect} = this.props;
const {items, focus, onSelect, onHighlight} = this.props;
const {rotateIndex, selectedIndex} = this.state;
const hasLimit = this.hasLimit();
const limit = this.getLimit();
@@ -98,21 +100,31 @@ class SelectInput extends PureComponent {
const lastIndex = (hasLimit ? limit : items.length) - 1;
const atFirstIndex = selectedIndex === 0;
const nextIndex = (hasLimit ? selectedIndex : lastIndex);
const nextRotateIndex = atFirstIndex ? rotateIndex + 1 : rotateIndex;
const nextSelectedIndex = atFirstIndex ? nextIndex : selectedIndex - 1;

this.setState({
rotateIndex: atFirstIndex ? rotateIndex + 1 : rotateIndex,
selectedIndex: atFirstIndex ? nextIndex : selectedIndex - 1
rotateIndex: nextRotateIndex,
selectedIndex: nextSelectedIndex
});

const slicedItems = hasLimit ? arrRotate(items, nextRotateIndex).slice(0, limit) : items;
onHighlight(slicedItems[nextSelectedIndex]);
}

if (s === ARROW_DOWN || s === 'j') {
const atLastIndex = selectedIndex === (hasLimit ? limit : items.length) - 1;
const nextIndex = (hasLimit ? selectedIndex : 0);
const nextRotateIndex = atLastIndex ? rotateIndex - 1 : rotateIndex;
const nextSelectedIndex = atLastIndex ? nextIndex : selectedIndex + 1;

this.setState({
rotateIndex: atLastIndex ? rotateIndex - 1 : rotateIndex,
selectedIndex: atLastIndex ? nextIndex : selectedIndex + 1
rotateIndex: nextRotateIndex,
selectedIndex: nextSelectedIndex
});

const slicedItems = hasLimit ? arrRotate(items, nextRotateIndex).slice(0, limit) : items;
onHighlight(slicedItems[nextSelectedIndex]);
}

if (s === ENTER) {
23 changes: 23 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
@@ -526,3 +526,26 @@ test('list - rotate when there are more items than limit', t => {

t.is(actual.lastFrame(), expected.lastFrame());
});

test('list - onHighlight', t => {
const items = [{
label: 'First',
value: 'first'
}, {
label: 'Second',
value: 'second'
}, {
label: 'Third',
value: 'third'
}];

const onHighlight = spy();
const {stdin} = render(<SelectInput items={items} limit={2} onHighlight={onHighlight}/>);

stdin.write(ARROW_DOWN);
stdin.write(ARROW_DOWN);

t.true(onHighlight.calledTwice);
t.deepEqual(onHighlight.firstCall.args[0], items[1]);
t.deepEqual(onHighlight.secondCall.args[0], items[2]);
});