Skip to content

Commit cd88905

Browse files
committed
refactor AutosizeInput.tsx component; update README.md; general code refactoring
1 parent d840af7 commit cd88905

File tree

11 files changed

+84
-88
lines changed

11 files changed

+84
-88
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ type SingleSelectDemoProps = Readonly<{
6161
isDisabled: boolean;
6262
}>;
6363

64-
const _cityOptions: Option[] = [
64+
const CITY_OPTIONS: Option[] = [
6565
{ id: 1, city: 'Austin', state: 'TX' },
6666
{ id: 2, city: 'Denver', state: 'CO' },
6767
{ id: 3, city: 'Chicago', state: 'IL' },
@@ -92,7 +92,7 @@ const SingleSelectDemo: FunctionComponent<SingleSelectDemoProps> = ({ isDisabled
9292
<Select
9393
isClearable
9494
isInvalid={isInvalid}
95-
options={_cityOptions}
95+
options={CITY_OPTIONS}
9696
isDisabled={isDisabled}
9797
onOptionChange={onOptionChange}
9898
getOptionValue={getOptionValue}
@@ -126,6 +126,7 @@ All properties are technically optional (with a few having default values). Very
126126
|`isLoading`| bool | `false` | Is the select in a state of loading - shows loading dots animation
127127
|`isInvalid`| bool | `false` | Is the current value invalid - control recieves invalid styling
128128
|`inputDelay`| number | `undefined` | The debounce delay in for the input search (milliseconds)
129+
|`pageSize`| number | `5` | Number of options to jump in menu when page{up|down} keys are used
129130
|`isDisabled`| bool | `false` | Is the select control disabled - recieves disabled styling
130131
|`required`| bool | `false` | Is the select control required - applied to the `input` element. When `true`, the optionally specified CSS from the `themeConfig.input.cssRequired` field will be applied to the `input` element.
131132
|`placeholder`| string | `Select option..` | Placeholder text for the select value

__stories__/helpers/constants/markup.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ export const CLASS_NAME_HTML =
1717
<div class="${CONTROL_CONTAINER_CLS}">
1818
<div>
1919
<div>${PLACEHOLDER_DEFAULT}</div>
20-
<input
21-
value=""
22-
type="text"
23-
class="${AUTOSIZE_INPUT_CLS}"
24-
/>
20+
<div>
21+
<input
22+
value=""
23+
type="text"
24+
class="${AUTOSIZE_INPUT_CLS}"
25+
/>
26+
</div>
2527
</div>
2628
<div>
2729
<div>

src/Select.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -325,10 +325,7 @@ const Select = forwardRef<SelectRef, SelectProps>((
325325
const blurInput = (): void => inputRef.current?.blur();
326326
const focusInput = (): void => inputRef.current?.focus();
327327
const scrollToItemIndex = (idx: number): void => listRef.current?.scrollToItem(idx);
328-
329328
const hasSelectedOptions = isArrayWithLength(selectedOption);
330-
const showClear = !!isClearable && !isDisabled && hasSelectedOptions;
331-
const inputReadOnly = isDisabled || !isSearchable || !!focusedMultiValue;
332329

333330
const openMenuAndFocusOption = useCallback((position: OptionIndexEnum): void => {
334331
if (!isArrayWithLength(menuOptions)) {
@@ -656,18 +653,18 @@ const Select = forwardRef<SelectRef, SelectProps>((
656653
if (isNotInput) e.preventDefault();
657654
};
658655

656+
const handleOnInputFocus = (e: FocusEvent<HTMLInputElement>): void => {
657+
onInputFocus?.(e);
658+
setIsFocused(true);
659+
};
660+
659661
const handleOnInputBlur = (e: FocusEvent<HTMLInputElement>): void => {
660662
onInputBlur?.(e);
661663
setIsFocused(false);
662664
setMenuOpen(false);
663665
setInputValue('');
664666
};
665667

666-
const handleOnInputFocus = (e: FocusEvent<HTMLInputElement>): void => {
667-
onInputFocus?.(e);
668-
setIsFocused(true);
669-
};
670-
671668
const handleOnInputChange = (e: FormEvent<HTMLInputElement>): void => {
672669
onChangeEvtValue.current = true;
673670
onInputChange?.(e.currentTarget.value);
@@ -692,6 +689,9 @@ const Select = forwardRef<SelectRef, SelectProps>((
692689
}
693690
}, [isDisabled, openMenuOnClick, openMenuAndFocusOption]);
694691

692+
const showClear = !!isClearable && !isDisabled && hasSelectedOptions;
693+
const inputReadOnly = isDisabled || !isSearchable || !!focusedMultiValue;
694+
695695
return (
696696
<ThemeProvider theme={theme}>
697697
<SelectWrapper

src/components/AutosizeInput/index.tsx

Lines changed: 34 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -63,43 +63,41 @@ const Input = styled.input.attrs(AUTOSIZE_INPUT_ATTRS) <InputProps>`
6363
${({ theme }) => theme.input.css}
6464
`;
6565

66-
const AutosizeInput = forwardRef<HTMLInputElement, AutosizeInputProps>(
67-
(
68-
{
69-
id,
70-
onBlur,
71-
onFocus,
72-
readOnly,
73-
required,
74-
onChange,
75-
ariaLabel,
76-
inputValue,
77-
ariaLabelledBy,
78-
hasSelectedOptions
79-
},
80-
ref: Ref<HTMLInputElement>
81-
) => {
82-
const isInvalid = !!required && !hasSelectedOptions;
66+
const AutosizeInput = forwardRef<HTMLInputElement, AutosizeInputProps>((
67+
{
68+
id,
69+
onBlur,
70+
onFocus,
71+
readOnly,
72+
required,
73+
onChange,
74+
ariaLabel,
75+
inputValue,
76+
ariaLabelledBy,
77+
hasSelectedOptions
78+
},
79+
ref: Ref<HTMLInputElement>
80+
) => {
81+
const isInvalid = !!required && !hasSelectedOptions;
8382

84-
return (
85-
<InputWrapper data-value={inputValue}>
86-
<Input
87-
id={id}
88-
ref={ref}
89-
isInvalid
90-
onBlur={onBlur}
91-
onFocus={onFocus}
92-
value={inputValue}
93-
readOnly={readOnly}
94-
required={isInvalid}
95-
aria-label={ariaLabel}
96-
aria-labelledby={ariaLabelledBy}
97-
onChange={!readOnly ? onChange : undefined}
98-
/>
99-
</InputWrapper>
100-
);
101-
}
102-
);
83+
return (
84+
<InputWrapper data-value={inputValue}>
85+
<Input
86+
id={id}
87+
ref={ref}
88+
isInvalid
89+
onBlur={onBlur}
90+
onFocus={onFocus}
91+
value={inputValue}
92+
readOnly={readOnly}
93+
required={isInvalid}
94+
aria-label={ariaLabel}
95+
aria-labelledby={ariaLabelledBy}
96+
onChange={!readOnly ? onChange : undefined}
97+
/>
98+
</InputWrapper>
99+
);
100+
});
103101

104102
AutosizeInput.displayName = 'AutosizeInput';
105103

src/components/IndicatorIcons/ClearSvgIcon.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,4 @@ const ClearSvgIcon: FunctionComponent = () => (
2626
</ClearSvg>
2727
);
2828

29-
3029
export default ClearSvgIcon;

src/components/Value/MultiValue.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ const MultiValue = memo<MultiValueProps>(({
7575
onMouseDown={suppressEvent}
7676
data-testid={CLEAR_ICON_MV_TESTID}
7777
>
78-
78+
&#10006;
7979
</Clear>
8080
</MultiValueWrapper>
8181
);

src/components/Value/index.tsx

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { Fragment, type ReactNode, type FunctionComponent } from 'react';
22
import MultiValue from './MultiValue';
3+
import styled from 'styled-components';
34
import { isArrayWithLength } from '../../utils';
4-
import styled, { css } from 'styled-components';
55
import type { MultiParams, SelectedOption, RenderLabelCallback } from '../../types';
66

77
type ValueProps = Readonly<{
@@ -15,22 +15,17 @@ type ValueProps = Readonly<{
1515
renderMultiOptions?: (params: MultiParams) => ReactNode;
1616
}>;
1717

18-
const SINGLE_VALUE_BASE_STYLE = css`
18+
const SingleValue = styled.div`
1919
margin: 0 2px;
20+
max-width: 100%;
2021
overflow: hidden;
2122
white-space: nowrap;
2223
box-sizing: border-box;
2324
text-overflow: ellipsis;
2425
grid-area: 1 / 1 / 2 / 3;
2526
`;
2627

27-
const SingleValue = styled.div`
28-
${SINGLE_VALUE_BASE_STYLE}
29-
max-width: 100%;
30-
`;
31-
32-
const Placeholder = styled.div`
33-
${SINGLE_VALUE_BASE_STYLE}
28+
const Placeholder = styled(SingleValue)`
3429
color: ${({ theme }) => theme.color.placeholder};
3530
`;
3631

src/constants/theme.ts

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
import type { DefaultTheme } from 'styled-components';
22
import { BOUNCE_ANIMATION_CSS, FADE_IN_ANIMATION_CSS } from './styled';
33

4-
/**
5-
* A contextual styled-components DefaultTheme object with default key-value pairs for CSS props.
6-
*/
4+
const color = {
5+
border: '#ced4da',
6+
danger: '#dc3545',
7+
primary: '#007bff',
8+
disabled: '#e9ecef',
9+
placeholder: '#6E7276',
10+
dangerLight: 'rgba(220, 53, 69, 0.25)'
11+
};
12+
713
export const DEFAULT_THEME: DefaultTheme = {
8-
color: {
9-
border: '#ced4da',
10-
danger: '#dc3545',
11-
primary: '#007bff',
12-
disabled: '#e9ecef',
13-
placeholder: '#6E7276',
14-
dangerLight: 'rgba(220, 53, 69, 0.25)'
15-
},
14+
color,
1615
input: {},
1716
select: {},
1817
loader: {
@@ -58,8 +57,8 @@ export const DEFAULT_THEME: DefaultTheme = {
5857
option: {
5958
textAlign: 'left',
6059
selectedColor: '#fff',
61-
selectedBgColor: '#007bff',
6260
padding: '0.375rem 0.75rem',
61+
selectedBgColor: color.primary,
6362
focusedBgColor: 'rgba(0, 123, 255, 0.15)'
6463
}
6564
},
@@ -85,7 +84,7 @@ export const DEFAULT_THEME: DefaultTheme = {
8584
color: '#a6a6a6',
8685
fontSize: '0.65em',
8786
alignSelf: 'center',
88-
focusColor: '#808080',
87+
focusColor: color.danger,
8988
transition: 'color 0.2s ease-out'
9089
}
9190
}

src/hooks/useCallbackRef.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,19 @@ import { useEffect, useRef, useCallback } from 'react';
77
*
88
* @param callback the callback to write to a ref object
99
*/
10-
const useCallbackRef = <T extends CallbackFn>(
11-
callback: T | undefined
12-
): T => {
10+
const useCallbackRef = <T extends CallbackFn>(callback: T | undefined): T => {
1311
const callbackRef = useRef(callback);
1412

1513
useEffect(() => {
1614
callbackRef.current = callback;
17-
});
15+
}, [callback]);
1816

19-
return useCallback(((...args) => callbackRef.current?.(...args)) as T, []);
17+
return useCallback(
18+
((...args) => {
19+
return callbackRef.current?.(...args);
20+
}) as T,
21+
[]
22+
);
2023
};
2124

2225
export default useCallbackRef;

src/hooks/useMenuOptions.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,12 @@ const useMenuOptions = (
3232
): MenuOption[] => {
3333
const getFilterOptionStringRef = useCallbackRef(getFilterOptionString || FUNCTIONS.optionFilter);
3434
const getIsOptionDisabledRef = useCallbackRef(getIsOptionDisabled || FUNCTIONS.isOptionDisabled);
35-
36-
const searchValue = !async ? debouncedInputValue : ''; // Prevent recomputing/filtering on input mutations in async mode
37-
const isFilterMatchAny = filterMatchFrom === FilterMatchEnum.ANY;
3835
const hideSelectedOptionsOrDefault = isBoolean(hideSelectedOptions) ? hideSelectedOptions : isMulti;
36+
const searchValue = !async ? debouncedInputValue : ''; // Prevent recomputing/filtering on input mutations in async mode
3937

4038
const menuOptions = useMemo<MenuOption[]>(() => {
4139
const selectedValues = selectedOption.map((x) => x.value);
40+
const isFilterMatchAny = filterMatchFrom === FilterMatchEnum.ANY;
4241
const matchVal = trimAndFormatFilterStr(searchValue, filterIgnoreCase, filterIgnoreAccents);
4342

4443
const isOptionFilterMatch = (option: MenuOption): boolean => {
@@ -54,8 +53,9 @@ const useMenuOptions = (
5453
const isDisabled = getIsOptionDisabledRef(data);
5554
const isSelected = selectedValues.includes(value);
5655
const menuOption: MenuOption = { data, value, label, isDisabled, isSelected };
57-
const filterOut = !isOptionFilterMatch(menuOption) || (hideSelectedOptionsOrDefault && isSelected);
58-
return filterOut ? undefined : menuOption;
56+
return (!isOptionFilterMatch(menuOption) || (hideSelectedOptionsOrDefault && isSelected))
57+
? undefined
58+
: menuOption;
5959
};
6060

6161
return options.reduce((acc: MenuOption[], option: OptionData) => {
@@ -69,7 +69,7 @@ const useMenuOptions = (
6969
getOptionValue,
7070
getOptionLabel,
7171
selectedOption,
72-
isFilterMatchAny,
72+
filterMatchFrom,
7373
filterIgnoreCase,
7474
filterIgnoreAccents,
7575
getIsOptionDisabledRef,

src/utils/common.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import type { SyntheticEvent } from 'react';
22
import type { SelectedOption, OptionValueCallback, OptionLabelCallback, CallbackFn } from '../types';
33
import { OPTION_CLS, EMPTY_ARRAY, OPTION_FOCUSED_CLS, OPTION_SELECTED_CLS, OPTION_DISABLED_CLS } from '../constants';
44

5-
const STYLED_ANI_KEY = 'animation';
65
const DIACRITICS_REG_EXP = /[\u0300-\u036f]/g;
76

87
/**
@@ -99,7 +98,7 @@ export const mergeDeep = <T>(target: any, source: any): T => {
9998
Object.keys(source).forEach((key) => {
10099
const sourceProp = source[key];
101100
output[key] =
102-
(key !== STYLED_ANI_KEY && isPlainObject(sourceProp))
101+
(key !== 'animation' && isPlainObject(sourceProp))
103102
? target[key]
104103
? mergeDeep(target[key], sourceProp)
105104
: sourceProp

0 commit comments

Comments
 (0)