Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Typescript Definitions and Usage Examples #37

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ gulp.task('test-script-format', () => (
gulp.src([
'./examples/src/**.js',
'./src/**/*.js',
'./test/**/*.js',
'./test/unit/**/*.js',
'./*.js',
])
.pipe(eslint())
Expand All @@ -27,11 +27,11 @@ gulp.task('test-script-format', () => (
));

gulp.task('test-mocha', () => (
gulp.src(['./test/**/*.js'])
gulp.src(['./test/unit/**/*.js'])
.pipe(mocha({
require: [
'babel-register',
'./test/setup.js',
'./test/unit/setup.js',
],
}))
));
Expand Down
230 changes: 230 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
import * as React from 'react';

/** A value-based option. */
export interface ValueOption<T> {
/** The option label. */
label: string;
/** The option value. */
value: T;
}

/** A category with other categories and values. */
export interface CategoryOption<T> {
/** The category label. */
label: string;
/** The category child options. */
options: Option<T>[];
}

/** Valid options include values and categories. */
export type Option<T> = ValueOption<T> | CategoryOption<T>;

/** A filter. */
export interface Filter<T> {
/** Available options. */
available: T[];
/** Selected options. */
selected: T[];
}

/** Properties common to every `DualListBox`. */
export interface CommonProperties<T> {
/**
* Available options.
*
* @example
* const options = [
* { value: 'one', label: 'One'},
* { value: 'two', label: 'Two'},
* ];
* <DualListBox options={options} />
*/
options: Option<T>[];
/**
* Selected options.
*
* @example
* <DualListBox options={options} selected={['one']} />
*/
selected?: T[];
/**
* Override the default center alignment of action buttons.
*
* @default "center"
*
* @example
* <DualListBox options={options} alignActions="top" />
*/
alignActions?: 'top' | 'center';
/**
* This flag will preserve the selection order. By default, `react-dual-listbox`
* orders selected items according to the order of the `options` property.
*
* @example
* <DualListBox options={options} preserveSelectOrder={true} />
*/
preserveSelectOrder?: boolean;
/**
* Restrict available options.
*
* @example
* const available = ['io', 'europa', 'ganymede', 'callisto'];
* <DualListBox options={options} available={available} />;
*/
available?: T[];
/**
* The display name for the hidden label for the available options control group.
*
* @default "Available"
*
* @example
* <DualListBox
* options={options}
* available={available}
* availableLabel="Available"
* />;
*/
availableLabel?: string;
/**
* The key codes that will trigger a toggle of the selected options.
*
* @default [13, 32]
*
* @example
* <DualListBox options={options} moveKeyCodes={[13, 32]} />
*/
moveKeyCodes?: number[];
/**
* The display name for the hidden label for the selected options control group.
*
* @default "Selected"
*
* @example
* <DualListBox options={options} selected="Selected" />
*/
selectedLabel?: string;
}

/** Additional `DualListBox` properties with filter. */
export interface FilterProperties<T, F extends boolean> {
/**
* Flag that determines whether filtering is enabled.
*
* @default false
*
* @example
* <DualListBox options={options} canFilter={true} />
*/
canFilter?: F;
/**
* Override the default filtering function.
*
* @example
* <DualListBox
* options={options}
* canFilter={true}
* filterCallback={(option, filterInput) => !!(...)}
* />
*/
filterCallback?: F extends true ? ((option: Option<T>, filterInput: string) => boolean) : void;
/**
* Override the default filter placeholder.
*
* @example
* <DualListBox
* options={options}
* canFilter={true}
* filterPlaceholder="..."
* />
*/
filterPlaceholder?: F extends true ? string : void;
/**
* Control the filter search text.
*
* @example
* const filter = { available: 'europa', selected: '' };
* <DualListBox
* options={options}
* canFilter={true}
* filter={filter}
* />
*/
filter?: Filter<T>;
/**
* Handle filter change.
*
* @example
* <DualListBox
* options={options}
* canFilter={true}
* onFilterChange={filter => {...}}
* />
*/
onFilterChange?: F extends true ? ((filter: string) => void) : void;
}

/** Additional `DualListBox` properties with complex selected values. */
export interface ValueProperties<T, V extends boolean> {
/**
* Handle selection changes.
*
* @example
* <DualListBox options={options} onChange={selected => {...}} />
*/
// onChange?: (selected: (T | Option<T>)[]) => void;
onChange?: (selected: (V extends true ? T[] : Option<T>[])) => void;
/**
* If true, the selected value passed in onChange is an array of string values.
* Otherwise, it is an array of options.
*
* @default true
*
* @example
* <DualListBox
* options={options}
* onChange={selectedValues => {...}}
* />
* <DualListBox
* options={options}
* simpleValue={false}
* onChange={selectedOptions => {...}}
* />
*/
simpleValue?: V;
}

/** `DualListBox` component properties. */
// export type DualListBoxProperties<P> = CommonProperties<P> & FilterProperties<P> & ValueProperties<P>;
interface DualListBoxProperties<P, F extends boolean, V extends boolean> extends CommonProperties<P>, FilterProperties<P, F>, ValueProperties<P, V> {}

/**
* A feature-rich dual list box for `React`.
*
* The `DualListBox` is a controlled component, so you have to update the
* `selected` property in conjunction with the `onChange` handler if you
* want the selected values to change.
*
* @example
* // Example options (Option<string>[]).
* const options = [
* { label: 'One', value: 'one' },
* { label: 'Two', value: 'two' },
* ];
*
* // Component state definition
* interface MinimalComponentState { selectedValues: string[]; }
* state: MinimalComponentState = { selectedValues: [] };
*
* // Component handler
* handleChange = (selectedValues: string[]) =>
* this.setState({ selectedValues });
*
* // Usage example (`DualListBox` with options of
* // `Options<string>[]` is a `DualListBox<string>`):
* <DualListBox
* options={options}
* selected={this.state.selectedValues}
* onChange={this.handleChange}
* />
*/
export default class DualListBox<P, F extends boolean = false, V extends boolean = true> extends React.Component<DualListBoxProperties<P, F, V>> {}
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,21 @@
},
"bugs": "https://github.com/jakezatecky/react-dual-listbox/issues",
"main": "lib/index.js",
"typings": "./index.d.ts",
"scripts": {
"build": "gulp build",
"examples": "gulp examples",
"gh-deploy": "git subtree push --prefix examples/dist origin gh-pages",
"prepublishOnly": "gulp build",
"test": "gulp test"
"test:unit": "gulp test",
"test:ts": "tsc --noEmit",
"test": "concurrently -n \"unit,ts\" -c \"green,blue\" \"npm run test:unit\" \"npm run test:ts\""
},
"peerDependencies": {
"react": "^15.0.0 || ^16.0.0"
},
"devDependencies": {
"@types/react": "^15",
"babel-core": "^6.4.5",
"babel-eslint": "^8.0.0",
"babel-loader": "^7.0.0",
Expand All @@ -38,6 +42,7 @@
"babel-preset-stage-2": "^6.3.13",
"browser-sync": "^2.18.6",
"chai": "^4.0.1",
"concurrently": "^3.5.1",
"enzyme": "^2.7.1",
"eslint": "^4.3.0",
"eslint-config-takiyon-react": "^0.3.0",
Expand All @@ -58,6 +63,7 @@
"react-addons-test-utils": "^15.4.2",
"react-dom": "^15.0.0",
"react-test-renderer": "^15.5.4",
"typescript": "^2.8.0-dev.20180211",
"webpack": "^3.0.0",
"webpack-stream": "^4.0.0"
},
Expand Down
Loading