Skip to content

Commit f63b836

Browse files
Add generic option type
1 parent cd1a0a7 commit f63b836

22 files changed

Lines changed: 106 additions & 74 deletions

src/behaviors/async.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export interface UseAsyncProps<Option extends OptionType> extends TypeaheadCompo
6666
useCache?: boolean;
6767
}
6868

69-
type Cache = Record<string, OptionType[]>;
69+
type Cache<Option extends OptionType> = Record<string, Option[]>;
7070

7171
interface DebouncedFunction extends Function {
7272
cancel(): void;
@@ -96,7 +96,7 @@ export function useAsync<Option extends OptionType>(props: UseAsyncProps<Option>
9696
...otherProps
9797
} = props;
9898

99-
const cacheRef = useRef<Cache>({});
99+
const cacheRef = useRef<Cache<Option>>({});
100100
const handleSearchDebouncedRef = useRef<DebouncedFunction | null>(null);
101101
const queryRef = useRef<string>(props.defaultInputValue || '');
102102

@@ -179,16 +179,15 @@ export function useAsync<Option extends OptionType>(props: UseAsyncProps<Option>
179179
}
180180

181181
/* istanbul ignore next */
182-
export function withAsync<T extends UseAsyncProps = UseAsyncProps>(
182+
export function withAsync<Option extends OptionType, T extends UseAsyncProps<Option> = UseAsyncProps<Option>>(
183183
Component: ComponentType<T>
184184
) {
185185
warn(
186-
false,
187-
'Warning: `withAsync` is deprecated and will be removed in the next ' +
186+
false,
187+
'Warning: `withAsync` is deprecated and will be removed in the next ' +
188188
'major version. Use `useAsync` instead.'
189189
);
190-
191-
const AsyncTypeahead = forwardRef<Typeahead, T>((props, ref) => (
190+
const AsyncTypeahead = forwardRef<Typeahead<Option>, T>((props, ref) => (
192191
<Component {...props} {...useAsync(props)} ref={ref} />
193192
));
194193

src/behaviors/item.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,19 @@ const propTypes = {
2626
position: PropTypes.number,
2727
};
2828

29-
export interface UseItemProps<T> extends HTMLProps<T> {
29+
export interface UseItemProps<T, Option extends OptionType> extends HTMLProps<T> {
3030
onClick?: MouseEventHandler<T>;
31-
option: OptionType;
31+
option: Option;
3232
position: number;
3333
}
3434

35-
export function useItem<T extends HTMLElement>({
35+
export function useItem<T extends HTMLElement, Option extends OptionType>({
3636
label,
3737
onClick,
3838
option,
3939
position,
4040
...props
41-
}: UseItemProps<T>) {
41+
}: UseItemProps<T, Option>) {
4242
const {
4343
activeIndex,
4444
id,
@@ -47,7 +47,7 @@ export function useItem<T extends HTMLElement>({
4747
onInitialItemChange,
4848
onMenuItemClick,
4949
setItem,
50-
} = useTypeaheadContext();
50+
} = useTypeaheadContext<Option>();
5151

5252
const itemRef = useRef<T>(null);
5353

@@ -101,7 +101,7 @@ export function useItem<T extends HTMLElement>({
101101
}
102102

103103
/* istanbul ignore next */
104-
export function withItem<T extends UseItemProps<HTMLElement>>(
104+
export function withItem<Option extends OptionType, T extends UseItemProps<HTMLElement, Option>>(
105105
Component: ComponentType<T>
106106
) {
107107
warn(

src/behaviors/token.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ export function useToken<T extends HTMLElement, Option extends OptionType>({
8989
}
9090

9191
/* istanbul ignore next */
92-
export function withToken<T extends UseTokenProps<HTMLElement>>(
92+
export function withToken<Option extends OptionType, T extends UseTokenProps<HTMLElement, Option>>(
9393
Component: ComponentType<T>
9494
) {
9595
warn(

src/components/MenuItem/MenuItem.stories.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,19 @@ import { Story, Meta } from '@storybook/react';
55

66
import MenuItem, { MenuItemProps } from './MenuItem';
77
import {
8+
createTypeaheadContext,
89
defaultContext,
9-
TypeaheadContext,
1010
TypeaheadContextType,
1111
} from '../../core/Context';
12-
import {OptionType} from "../../types";
12+
import {TestOption} from "../../tests/data";
1313

1414
export default {
1515
title: 'Components/MenuItem/MenuItem',
1616
component: MenuItem,
1717
} as Meta;
1818

1919
interface Args {
20-
context: Partial<TypeaheadContextType<OptionType>>;
20+
context: Partial<TypeaheadContextType<TestOption>>;
2121
props: MenuItemProps;
2222
}
2323

@@ -26,11 +26,12 @@ const value = {
2626
id: 'test-id',
2727
};
2828

29-
const Template: Story<Args> = ({ context, props }) => (
30-
<TypeaheadContext.Provider value={{ ...value, ...context }}>
29+
const Template: Story<Args> = ({ context, props }) => {
30+
const TypeaheadContext = createTypeaheadContext<TestOption>()
31+
return <TypeaheadContext.Provider value={{...value, ...context}}>
3132
<MenuItem {...props} />
3233
</TypeaheadContext.Provider>
33-
);
34+
};
3435

3536
export const Default = Template.bind({});
3637
Default.args = {

src/components/MenuItem/MenuItem.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import cx from 'classnames';
22
import React, { forwardRef, HTMLAttributes, MouseEvent } from 'react';
33

44
import { useItem, UseItemProps } from '../../behaviors/item';
5+
import {OptionType} from "../../types";
56

67
export interface BaseMenuItemProps extends HTMLAttributes<HTMLAnchorElement> {
78
active?: boolean;
@@ -27,8 +28,8 @@ export const BaseMenuItem = forwardRef<HTMLAnchorElement, BaseMenuItemProps>(
2728
}
2829
);
2930

30-
export type MenuItemProps = UseItemProps<HTMLAnchorElement>;
31+
export type MenuItemProps<Option extends OptionType> = UseItemProps<HTMLAnchorElement, Option>;
3132

32-
export default function MenuItem(props: MenuItemProps) {
33+
export default function MenuItem<Option extends OptionType>(props: MenuItemProps<Option>) {
3334
return <BaseMenuItem {...useItem(props)} />;
3435
}

src/components/Token/Token.stories.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ import { Story, Meta } from '@storybook/react';
55

66
import Token, { TokenProps } from './Token';
77
import { noop } from '../../utils';
8-
import {OptionType} from "../../types";
8+
import {TestOption} from "../../tests/data";
99

1010
export default {
1111
title: 'Components/Token',
1212
component: Token,
1313
} as Meta;
1414

15-
const Template: Story<TokenProps<HTMLElement, OptionType>> = (args) => <Token {...args} />;
15+
const Template: Story<TokenProps<HTMLElement, TestOption>> = (args) => <Token {...args} />;
1616

1717
export const Interactive = Template.bind({});
1818
Interactive.args = {

src/components/TypeaheadInputMulti/TypeaheadInputMulti.stories.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import TypeaheadInputMulti, {
88
TypeaheadInputMultiProps,
99
} from './TypeaheadInputMulti';
1010

11-
import options from '../../tests/data';
11+
import options, {TestOption} from '../../tests/data';
1212
import { HintProvider, noop } from '../../tests/helpers';
1313
import type { Size } from '../../types';
1414
import {OptionType} from "../../types";
@@ -36,7 +36,7 @@ interface Args<Option extends OptionType> extends TypeaheadInputMultiProps<Optio
3636
size?: Size;
3737
}
3838

39-
const Template: Story<Args> = ({ hintText = '', ...args }) => {
39+
const Template: Story<Args<TestOption>> = ({ hintText = '', ...args }) => {
4040
const [value, setValue] = useState(args.value);
4141
const [inputNode, setInputNode] = useState<HTMLInputElement | null>(null);
4242

src/components/TypeaheadMenu/TypeaheadMenu.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const propTypes = {
4242
const defaultProps = {
4343
newSelectionPrefix: 'New selection: ',
4444
paginationText: 'Display additional results...',
45-
renderMenuItemChildren: (option: OptionType, props: TypeaheadMenuProps<OptionType>) => (
45+
renderMenuItemChildren: <Option extends OptionType>(option: Option, props: TypeaheadMenuProps<Option>) => (
4646
<Highlighter search={props.text}>
4747
{getOptionLabel(option, props.labelKey)}
4848
</Highlighter>

src/core/Context.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { createContext, useContext } from 'react';
22

3-
import { noop } from '../utils';
3+
import {noop, once} from '../utils';
44
import { Id, OptionType, OptionHandler, SelectEvent } from '../types';
55

66
export interface TypeaheadContextType<Option extends OptionType> {
@@ -31,7 +31,5 @@ export const defaultContext = {
3131
setItem: noop,
3232
};
3333

34-
export const TypeaheadContext =
35-
createContext<TypeaheadContextType<OptionType>>(defaultContext);
36-
37-
export const useTypeaheadContext = () => useContext(TypeaheadContext);
34+
export const createTypeaheadContext = once(<Option extends OptionType>() => createContext<TypeaheadContextType<Option>>(defaultContext));
35+
export const useTypeaheadContext = <Option extends OptionType>() => useContext(createTypeaheadContext<Option>());

src/core/TypeaheadManager.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { KeyboardEvent, useEffect, useRef } from 'react';
22

3-
import { TypeaheadContext, TypeaheadContextType } from './Context';
3+
import {createTypeaheadContext, TypeaheadContextType} from './Context';
44
import {
55
defaultSelectHint,
66
getHintText,
@@ -55,6 +55,8 @@ const contextKeys = [
5555
] as (keyof TypeaheadManagerProps<OptionType>)[];
5656

5757
const TypeaheadManager = <Option extends OptionType>(props: TypeaheadManagerProps<Option>) => {
58+
const TypeaheadContext = createTypeaheadContext<Option>()
59+
5860
const {
5961
allowNew,
6062
children,

0 commit comments

Comments
 (0)