Skip to content

Commit 78192bb

Browse files
committed
v4.0.0
1 parent 7725280 commit 78192bb

File tree

6 files changed

+56
-65
lines changed

6 files changed

+56
-65
lines changed

README.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,19 +46,17 @@ $ yarn add react-window styled-components react-functional-select
4646

4747
```jsx
4848
import { Select } from 'react-functional-select';
49-
import React, { useState, useEffect, useCallback } from 'react';
49+
import React, { useState, useEffect, useCallback, type ComponentProps } from 'react';
5050
import { Card, CardHeader, CardBody, Container, SelectContainer } from '../shared/components';
5151

52+
type SelectProps = ComponentProps<typeof Select>;
53+
5254
type Option = Readonly<{
5355
id: number;
5456
city: string;
5557
state: string;
5658
}>;
5759

58-
type SingleSelectProps = Readonly<{
59-
isDisabled: boolean;
60-
}>;
61-
6260
const CITY_OPTIONS: Option[] = [
6361
{ id: 1, city: 'Austin', state: 'TX' },
6462
{ id: 2, city: 'Denver', state: 'CO' },
@@ -67,16 +65,18 @@ const CITY_OPTIONS: Option[] = [
6765
{ id: 5, city: 'Houston', state: 'TX' }
6866
];
6967

70-
const SingleSelect: React.FC<SingleSelectProps> = ({ isDisabled }) => {
68+
const SingleSelect: React.FC<SelectProps> = ({ isDisabled }) => {
7169
const [isInvalid, setIsInvalid] = useState<boolean>(false);
7270
const [selectedOption, setSelectedOption] = useState<Option | null>(null);
7371

74-
const getOptionValue = useCallback((option: Option): number => option.id, []);
75-
const onOptionChange = useCallback((option: Option | null): void => setSelectedOption(option), []);
76-
const getOptionLabel = useCallback((option: Option): string => `${option.city}, ${option.state}`, []);
72+
const getOptionValue = useCallback((opt: Option): number => opt.id, []);
73+
const onOptionChange = useCallback((opt: Option | null): void => setSelectedOption(opt), []);
74+
const getOptionLabel = useCallback((opt: Option): string => `${opt.city}, ${opt.state}`, []);
7775

7876
useEffect(() => {
79-
isDisabled && setIsInvalid(false);
77+
if (isDisabled) {
78+
setIsInvalid(false);
79+
}
8080
}, [isDisabled]);
8181

8282
return (

__stories__/helpers/components/CodeMarkup.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { PrismLight as SyntaxHighlighter } from 'react-syntax-highlighter';
77
const dark = require('react-syntax-highlighter/dist/esm/styles/prism/dark').default;
88
const markup = require('react-syntax-highlighter/dist/esm/languages/prism/markup').default;
99
const javascript = require('react-syntax-highlighter/dist/esm/languages/prism/javascript').default;
10+
1011
SyntaxHighlighter.registerLanguage('markup', markup);
1112
SyntaxHighlighter.registerLanguage('javascript', javascript);
1213

__tests__/helpers/utils.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,7 @@ export const getOptionSingle = (index: number = 0): Option => ({ ...OPTIONS[inde
2222
export const getSelectedOptionSingle = (): SelectedOption[] => {
2323
const data = getOptionSingle();
2424
const { value, label } = data;
25-
const option: SelectedOption = { data, value, label };
26-
27-
return [option];
25+
return [{ data, value, label }];
2826
};
2927

3028
// ============================================
@@ -60,9 +58,15 @@ export const MENU_OPTIONS: MenuOption[] = [MENU_OPTION_SELECTED, MENU_OPTION_DIS
6058
// ============================================
6159

6260
export const stringifyCSSProperties = (obj: CSSProperties = {}): string => {
63-
const cssProps = Object.keys(obj).map((key) => `${key}: ${obj[key]};`);
64-
return cssProps.join(' ');
61+
return Object.keys(obj)
62+
.map((key) => `${key}: ${obj[key]};`)
63+
.join(' ');
6564
};
6665

6766
export const RENDER_OPTION_LABEL_MOCK = jest.fn(({ label }: OptionData): ReactNode => label);
68-
export const RENDER_MULTI_OPTIONS_MOCK = jest.fn(({ selected, renderOptionLabel }: MultiParams): ReactNode => selected.map((option) => renderOptionLabel(option.data)).join(', '));
67+
68+
export const RENDER_MULTI_OPTIONS_MOCK = jest.fn(
69+
({selected, renderOptionLabel}: MultiParams): ReactNode => {
70+
return selected.map((option) => renderOptionLabel(option.data)).join(', ');
71+
}
72+
);

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
"jest-environment-jsdom": "^29.3.1",
8989
"jest-enzyme": "^7.1.2",
9090
"npm-run-all": "^4.1.5",
91-
"prettier": "^2.7.1",
91+
"prettier": "^2.8.0",
9292
"react": "^18.2.0",
9393
"react-dom": "^18.2.0",
9494
"react-syntax-highlighter": "^15.5.0",

src/utils/common.ts

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,19 @@ import { OPTION_CLS, EMPTY_ARRAY, OPTION_FOCUSED_CLS, OPTION_SELECTED_CLS, OPTIO
55
const DIACRITICS_REG_EXP = /[\u0300-\u036f]/g;
66

77
/**
8-
* @private
9-
*
108
* Strips all diacritics from a string.
11-
* May not be supported by all legacy browsers (IE11 >=).
129
*/
13-
const stripDiacritics = (val: string): string => val.normalize('NFD').replace(DIACRITICS_REG_EXP, '');
10+
const stripDiacritics = (val: string): string => {
11+
return val.normalize('NFD').replace(DIACRITICS_REG_EXP, '');
12+
};
1413

15-
// Simple helper functions
1614
export const isBoolean = (val: unknown): val is boolean => typeof val === 'boolean';
1715
export const isFunction = (val: unknown): val is CallbackFn => typeof val === 'function';
1816
export const isArrayWithLength = (val: unknown): boolean => Array.isArray(val) && !!val.length;
1917
export const isPlainObject = (val: unknown): boolean => val !== null && typeof val === 'object' && !Array.isArray(val);
2018

2119
/**
22-
* Prevent default behavior and propagation of an event.
20+
* Prevent default behavior and propagation of an event
2321
*/
2422
export const suppressEvent = (e: SyntheticEvent<Element>): void => {
2523
e.preventDefault();
@@ -28,7 +26,7 @@ export const suppressEvent = (e: SyntheticEvent<Element>): void => {
2826

2927
/**
3028
* Apply regex to string, and if the value is NOT case sensitive,
31-
* call .toLowerCase() and return result.
29+
* call .toLowerCase() and return result
3230
*/
3331
export const trimAndFormatFilterStr = (
3432
value: string,
@@ -43,7 +41,7 @@ export const trimAndFormatFilterStr = (
4341
};
4442

4543
/**
46-
* Builds the className property in Option.tsx component.
44+
* Builds the className property in Option.tsx component
4745
*/
4846
export const buildOptionClsName = (
4947
isDisabled: boolean,
@@ -63,7 +61,7 @@ export const buildOptionClsName = (
6361
};
6462

6563
/**
66-
* Parses an object or an array of objects into output of SelectedOption[].
64+
* Parses an object or an array of objects into output of SelectedOption[]
6765
*/
6866
export const normalizeValue = (
6967
value: unknown,
@@ -88,9 +86,9 @@ export const normalizeValue = (
8886
};
8987

9088
/**
91-
* Immutable implementation of mergeDeep for two objects. Will return the merged result.
89+
* Immutable implementation of mergeDeep for two objects. Will return the merged result
9290
* In first condition of if/else block - check that property is no 'animation',
93-
* since we never want to merge that complex styled-component object.
91+
* since we never want to merge that complex styled-component object
9492
*/
9593
export const mergeDeep = <T>(target: any, source: any): T => {
9694
const output = { ...target };

src/utils/menu.ts

Lines changed: 25 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,33 @@
11
import type { CallbackFn } from '../types';
22

33
/**
4-
* @private
5-
*
64
* @param t: time (elapsed)
75
* @param b: initial value
86
* @param c: amount of change
97
* @param d: duration
108
*/
11-
function easeOutCubic(t: number, b: number, c: number, d: number): number {
9+
const easeOutCubic = (
10+
t: number,
11+
b: number,
12+
c: number,
13+
d: number
14+
): number => {
1215
return c * ((t = t / d - 1) * t * t + 1) + b;
13-
}
16+
};
1417

15-
/**
16-
* @private
17-
*/
18-
function getScrollTop(el: HTMLElement): number {
18+
const getScrollTop = (el: HTMLElement): number => {
1919
return isDocumentElement(el) ? window.pageYOffset : el.scrollTop;
20-
}
20+
};
2121

22-
/**
23-
* @private
24-
*/
25-
function scrollTo(el: HTMLElement, top: number): void {
22+
const scrollTo = (el: HTMLElement, top: number): void => {
2623
isDocumentElement(el) ? window.scrollTo(0, top) : (el.scrollTop = top);
27-
}
24+
};
2825

29-
/**
30-
* @private
31-
*/
32-
function isDocumentElement(el: HTMLElement | typeof window): boolean {
26+
const isDocumentElement = (el: HTMLElement | typeof window): boolean => {
3327
return el === document.body || el === document.documentElement || el === window;
34-
}
28+
};
3529

36-
/**
37-
* @private
38-
*/
39-
function getScrollParent(el: HTMLElement): HTMLElement {
30+
const getScrollParent = (el: HTMLElement): HTMLElement => {
4031
let style = getComputedStyle(el);
4132

4233
if (style.position === 'fixed') {
@@ -57,34 +48,31 @@ function getScrollParent(el: HTMLElement): HTMLElement {
5748
}
5849

5950
return document.documentElement;
60-
}
51+
};
6152

62-
/**
63-
* @private
64-
*/
65-
function smoothScrollTo(
53+
const smoothScrollTo = (
6654
el: HTMLElement,
6755
to: number,
6856
duration: number = 300,
6957
callback?: CallbackFn
70-
): void {
58+
): void => {
7159
let currentTime = 0;
7260
const start = getScrollTop(el);
7361
const change = to - start;
7462

75-
function scrollFn() {
63+
const scrollFn = () => {
7664
currentTime += 5;
7765
const calcScrollTop = easeOutCubic(currentTime, start, change, duration);
7866
scrollTo(el, calcScrollTop);
7967
(currentTime < duration) ? requestAnimationFrame(scrollFn) : callback?.();
80-
}
68+
};
8169

8270
requestAnimationFrame(scrollFn);
83-
}
71+
};
8472

8573
/**
86-
* Calculates the top property value for the MenuWrapper <div />.
87-
* This property is only generated when the position of the menu is above the control.
74+
* Calculates the top property value for the MenuWrapper element
75+
* This property is only generated when the position of the menu is above the control
8876
*/
8977
export const calculateMenuTop = (
9078
menuHeight: number,
@@ -110,7 +98,7 @@ export const menuFitsBelowControl = (el: HTMLElement | null): boolean => {
11098

11199
/**
112100
* Calculate space around the control and menu to determine if an animated
113-
* scroll can performed to show the menu in full view. Also, execute a callback if defined.
101+
* scroll can performed to show the menu in full view. Also, execute a callback if defined
114102
*/
115103
export const scrollMenuIntoViewOnOpen = (
116104
menuEl: HTMLElement | null,
@@ -138,8 +126,8 @@ export const scrollMenuIntoViewOnOpen = (
138126
const spaceBelow = scrollParent.getBoundingClientRect().height - scrollTop - top;
139127
const notEnoughSpaceBelow = spaceBelow < height;
140128

141-
// Sufficient space does not exist to scroll menu fully into view.
142-
// Calculate available space and use that as the the new menuHeight (use scrollSpaceBelow for now).
129+
// Sufficient space does not exist to scroll menu fully into view
130+
// Calculate available space and use that as the the new menuHeight (use scrollSpaceBelow for now)
143131
// OR scrollMenuIntoView = false
144132
if (notEnoughSpaceBelow || !scrollMenuIntoView) {
145133
const condensedMenuHeight = notEnoughSpaceBelow ? spaceBelow : undefined;

0 commit comments

Comments
 (0)