Skip to content

Commit 6d8ec94

Browse files
committed
New feature - addresses issue: To styling with different states #11 - expanded clearIcon and caretIcon prop types to now include function returning a JSX.Element so a partial of the wrapping component's state can be forwarded for the custom icon markup to react to; added unit tests for IndicatorIcons.tsx component; bump some dev dependencies
1 parent bf99aa0 commit 6d8ec94

File tree

53 files changed

+243
-208
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+243
-208
lines changed

.storybook/config/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import theme from './theme';
22
import GlobalStyle from './globalStyle';
3-
import { polyfillBrowserMS } from './polyfills';
3+
import { polyfillManager } from './polyfills';
44

55
export {
66
theme,
77
GlobalStyle,
8-
polyfillBrowserMS
8+
polyfillManager
99
};

.storybook/config/polyfills.js

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,30 @@
11
import { NativeEventSource, EventSourcePolyfill } from 'event-source-polyfill';
22

33
/**
4-
* 1). Polyfill EventSource for Edge and IE browsers.
4+
* 1). Polyfill EventSource for Edge/IE browser.
55
* 2). If IE, polyfill string.prototype.normalize().
6-
* Function 'normalizePolyfill' checks for IE or if window object is not intialized.
76
*/
8-
export function polyfillBrowserMS() {
9-
function normalizePolyfill() {
10-
const isWindowAvailable = Boolean(window && window.navigator);
11-
const isBrowserIE = (isWindowAvailable && /MSIE|Trident/.test(window.navigator.userAgent));
12-
return !isWindowAvailable || isBrowserIE;
13-
}
7+
export const polyfillManager = {
8+
isIE() {
9+
const isWindowAvailable = !!(window && window.navigator);
10+
return isWindowAvailable && /MSIE|Trident/.test(window.navigator.userAgent);
11+
},
12+
13+
polyfillEventSource() {
14+
global.EventSource = NativeEventSource || EventSourcePolyfill;
15+
},
1416

15-
global.EventSource = NativeEventSource || EventSourcePolyfill;
17+
/*
18+
polyfillNormalizeLib() {
19+
if (this.isIE()) {
20+
import('unorm').catch(e => console.error(e));
21+
}
22+
},
23+
*/
1624

17-
if (normalizePolyfill()) {
18-
import('unorm').catch((e) => console.error(e));
25+
polyfill() {
26+
this.polyfillEventSource();
27+
// this.polyfillNormalizeLib();
28+
return this;
1929
}
20-
}
30+
};

.storybook/preview.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import React from 'react';
22
import ReactDOM from 'react-dom';
33
import { toast } from 'react-toastify';
44
import { addParameters } from '@storybook/react';
5-
import { polyfillBrowserMS, GlobalStyle } from './config';
5+
import { GlobalStyle, polyfillManager } from './config';
66

7-
// Polyfill IE and Edge browsers
8-
polyfillBrowserMS();
7+
// Polyfill IE/Edge browsers
8+
polyfillManager.polyfill();
99

1010
// Define storybook global configuration
1111
addParameters({

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,8 @@ All properties are technically optional (with a few having default values). Very
129129
|`isClearable`| bool | `false` | Is the select value clearable
130130
|`noOptionsMsg`| string | `No options` | The text displayed in the menu when there are no options available
131131
|`loadingMsg`| string | `Loading...` | The text displayed in the menu when `isLoading` === `true`
132-
|`clearIcon`| ReactNode | `undefined` | Custom clear icon node
133-
|`caretIcon`| ReactNode | `undefined` | Custom caret icon node
132+
|`clearIcon`| ReactNode OR ((state: Partial\<IndicatorIconsProps\>) => ReactNode) | `undefined` | Custom clear icon node - `state` forwarded to a function is `{ menuOpen, isLoading, isInvalid, isDisabled }`
133+
|`caretIcon`| ReactNode OR ((state: Partial\<IndicatorIconsProps\>) => ReactNode) | `undefined` | Custom caret icon node - `state` forwarded to a function is `{ menuOpen, isLoading, isInvalid, isDisabled }`
134134
|`loadingNode`| ReactNode | `undefined` | Custom loading node
135135
|`options`| array | `[]` | The menu options
136136
|`isSearchable`| bool | `true` | Whether to enable search functionality or not

__tests__/IndicatorIcons.test.tsx

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { ReactNode } from 'react';
22
import DefaultThemeObj from '../src/theme';
33
import { ThemeProvider } from 'styled-components';
44
import { IndicatorIcons } from '../src/components';
@@ -37,17 +37,25 @@ const createIndicatorIconsProps = () => {
3737
};
3838
};
3939

40+
const customIconFn = ({ menuOpen, isLoading, isInvalid, isDisabled }: Partial<IndicatorIconsProps>): ReactNode => {
41+
const testIdText = `${menuOpen}_${isLoading}_${isInvalid}_${isDisabled}`;
42+
43+
return (
44+
<span data-testid={testIdText}>
45+
custom_icon
46+
</span>
47+
);
48+
};
49+
4050
// ============================================
4151
// Test cases
4252
// ============================================
4353

4454
test('clear icon has a static className (enables styling via classic CSS) when "addClassNames" = true', async () => {
4555
const { props } = createIndicatorIconsProps();
46-
const mergedProps = {
47-
...props,
48-
addClassNames: true,
49-
};
56+
const mergedProps = { ...props, addClassNames: true };
5057
const { getByTestId } = renderIndicatorIcons(mergedProps);
58+
5159
const firstChildOfClearIconElement = getByTestId(CLEAR_ICON_TESTID!).firstChild;
5260
expect(firstChildOfClearIconElement).toHaveClass(CLEAR_ICON_CLS);
5361
});
@@ -76,11 +84,9 @@ test('caret indicator has functioning "mouseDown" and "touchEnd" events', async
7684

7785
test('clear icon is not rendered and loading animation is rendered when "isLoading" = true', async () => {
7886
const { props } = createIndicatorIconsProps();
79-
const mergedProps = {
80-
...props,
81-
isLoading: true,
82-
};
87+
const mergedProps = { ...props, isLoading: true };
8388
const { queryByTestId } = renderIndicatorIcons(mergedProps);
89+
8490
expect(queryByTestId(CLEAR_ICON_TESTID!)).toBeNull();
8591
});
8692

@@ -99,30 +105,40 @@ test('loading can render as a custom node (instead of default LoadingDots.tsx co
99105
expect(getByText(loadingNodeText)).toBeInTheDocument();
100106
});
101107

102-
test('clear icon can render as a custom node', async () => {
108+
test('clear icon can render as a ReactNode', async () => {
103109
const clearIconText = 'clear-icon-node';
104110
const clearIcon = (<span>{clearIconText}</span>);
105111
const { props } = createIndicatorIconsProps();
106-
107-
const mergedProps = {
108-
...props,
109-
clearIcon,
110-
};
112+
const mergedProps = { ...props, clearIcon };
111113

112114
const { getByText } = renderIndicatorIcons(mergedProps);
113115
expect(getByText(clearIconText)).toBeInTheDocument();
114116
});
115117

116-
test('caret icon can render as a custom node', async () => {
118+
test('clear icon can render as a callback function with return type of ReactNode - callback accepts forwarded state props from wrapping component.', async () => {
119+
const customIconTestIdText = 'true_false_false_false';
120+
const { props } = createIndicatorIconsProps();
121+
const mergedProps = { ...props, menuOpen: true, clearIcon: customIconFn };
122+
123+
const { getByTestId } = renderIndicatorIcons(mergedProps);
124+
expect(getByTestId(customIconTestIdText)).toBeInTheDocument();
125+
});
126+
127+
test('caret icon can render as a ReactNode', async () => {
117128
const caretIconText = 'caret-icon-node';
118129
const caretIcon = (<span>{caretIconText}</span>);
119130
const { props } = createIndicatorIconsProps();
120-
121-
const mergedProps = {
122-
...props,
123-
caretIcon,
124-
};
131+
const mergedProps = { ...props, caretIcon };
125132

126133
const { getByText } = renderIndicatorIcons(mergedProps);
127134
expect(getByText(caretIconText)).toBeInTheDocument();
135+
});
136+
137+
test('caret icon can render as a callback function with return type of ReactNode - callback accepts forwarded state props from wrapping component.', async () => {
138+
const customIconTestIdText = 'true_false_false_false';
139+
const { props } = createIndicatorIconsProps();
140+
const mergedProps = { ...props, menuOpen: true, caretIcon: customIconFn };
141+
142+
const { getByTestId } = renderIndicatorIcons(mergedProps);
143+
expect(getByTestId(customIconTestIdText)).toBeInTheDocument();
128144
});

docs/3.a95d2874843b8cda282a.bundle.js

Lines changed: 0 additions & 1 deletion
This file was deleted.

docs/8.4e43a3f060f64a50053a.bundle.js

Lines changed: 0 additions & 2 deletions
This file was deleted.

docs/8.4e43a3f060f64a50053a.bundle.js.map

Lines changed: 0 additions & 1 deletion
This file was deleted.

docs/asset-manifest.json

Lines changed: 30 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,39 @@
11
{
22
"files": {
3-
"main~24120820.js": "./main~24120820.4e43a3f060f64a50053a.bundle.js",
4-
"main~24120820.js.map": "./main~24120820.4e43a3f060f64a50053a.bundle.js.map",
5-
"runtime~main.js": "./runtime~main.4e43a3f060f64a50053a.bundle.js",
6-
"runtime~main.js.map": "./runtime~main.4e43a3f060f64a50053a.bundle.js.map",
7-
"vendors~main~1f20a385.js": "./vendors~main~1f20a385.4e43a3f060f64a50053a.bundle.js",
8-
"vendors~main~1f20a385.js.map": "./vendors~main~1f20a385.4e43a3f060f64a50053a.bundle.js.map",
9-
"vendors~main~253ae210.js": "./vendors~main~253ae210.4e43a3f060f64a50053a.bundle.js",
10-
"vendors~main~253ae210.js.map": "./vendors~main~253ae210.4e43a3f060f64a50053a.bundle.js.map",
11-
"vendors~main~678f84af.js": "./vendors~main~678f84af.4e43a3f060f64a50053a.bundle.js",
12-
"vendors~main~678f84af.js.map": "./vendors~main~678f84af.4e43a3f060f64a50053a.bundle.js.map",
13-
"vendors~main~d939e436.js": "./vendors~main~d939e436.4e43a3f060f64a50053a.bundle.js",
14-
"vendors~main~d939e436.js.map": "./vendors~main~d939e436.4e43a3f060f64a50053a.bundle.js.map",
15-
"vendors~main~db300d2f.js": "./vendors~main~db300d2f.4e43a3f060f64a50053a.bundle.js",
16-
"vendors~main~db300d2f.js.map": "./vendors~main~db300d2f.4e43a3f060f64a50053a.bundle.js.map",
17-
"vendors~main~ec8c427e.js": "./vendors~main~ec8c427e.4e43a3f060f64a50053a.bundle.js",
18-
"vendors~main~ec8c427e.js.map": "./vendors~main~ec8c427e.4e43a3f060f64a50053a.bundle.js.map",
19-
"8.4e43a3f060f64a50053a.bundle.js": "./8.4e43a3f060f64a50053a.bundle.js",
20-
"8.4e43a3f060f64a50053a.bundle.js.map": "./8.4e43a3f060f64a50053a.bundle.js.map",
3+
"main~24120820.js": "./main~24120820.f0b69229287ff4cc0f0a.bundle.js",
4+
"main~24120820.js.map": "./main~24120820.f0b69229287ff4cc0f0a.bundle.js.map",
5+
"runtime~main.js": "./runtime~main.f0b69229287ff4cc0f0a.bundle.js",
6+
"runtime~main.js.map": "./runtime~main.f0b69229287ff4cc0f0a.bundle.js.map",
7+
"vendors~main~1f20a385.js": "./vendors~main~1f20a385.f0b69229287ff4cc0f0a.bundle.js",
8+
"vendors~main~1f20a385.js.map": "./vendors~main~1f20a385.f0b69229287ff4cc0f0a.bundle.js.map",
9+
"vendors~main~253ae210.js": "./vendors~main~253ae210.f0b69229287ff4cc0f0a.bundle.js",
10+
"vendors~main~253ae210.js.map": "./vendors~main~253ae210.f0b69229287ff4cc0f0a.bundle.js.map",
11+
"vendors~main~678f84af.js": "./vendors~main~678f84af.f0b69229287ff4cc0f0a.bundle.js",
12+
"vendors~main~678f84af.js.map": "./vendors~main~678f84af.f0b69229287ff4cc0f0a.bundle.js.map",
13+
"vendors~main~d939e436.js": "./vendors~main~d939e436.f0b69229287ff4cc0f0a.bundle.js",
14+
"vendors~main~d939e436.js.map": "./vendors~main~d939e436.f0b69229287ff4cc0f0a.bundle.js.map",
15+
"vendors~main~db300d2f.js": "./vendors~main~db300d2f.f0b69229287ff4cc0f0a.bundle.js",
16+
"vendors~main~db300d2f.js.map": "./vendors~main~db300d2f.f0b69229287ff4cc0f0a.bundle.js.map",
17+
"vendors~main~ec8c427e.js": "./vendors~main~ec8c427e.f0b69229287ff4cc0f0a.bundle.js",
18+
"vendors~main~ec8c427e.js.map": "./vendors~main~ec8c427e.f0b69229287ff4cc0f0a.bundle.js.map",
2119
"iframe.html": "./iframe.html",
22-
"precache-manifest.5bb453b63e27973bc7b2c19b1f3d2bb5.js": "./precache-manifest.5bb453b63e27973bc7b2c19b1f3d2bb5.js",
20+
"precache-manifest.1d2d8b302a4cf43288e09ef5c570c063.js": "./precache-manifest.1d2d8b302a4cf43288e09ef5c570c063.js",
2321
"service-worker.js": "./service-worker.js",
2422
"static/media/react-logo.svg": "./static/media/react-logo.9f16557d.svg",
25-
"vendors~main~1f20a385.4e43a3f060f64a50053a.bundle.js.LICENSE.txt": "./vendors~main~1f20a385.4e43a3f060f64a50053a.bundle.js.LICENSE.txt",
26-
"vendors~main~253ae210.4e43a3f060f64a50053a.bundle.js.LICENSE.txt": "./vendors~main~253ae210.4e43a3f060f64a50053a.bundle.js.LICENSE.txt",
27-
"vendors~main~678f84af.4e43a3f060f64a50053a.bundle.js.LICENSE.txt": "./vendors~main~678f84af.4e43a3f060f64a50053a.bundle.js.LICENSE.txt",
28-
"vendors~main~db300d2f.4e43a3f060f64a50053a.bundle.js.LICENSE.txt": "./vendors~main~db300d2f.4e43a3f060f64a50053a.bundle.js.LICENSE.txt",
29-
"vendors~main~ec8c427e.4e43a3f060f64a50053a.bundle.js.LICENSE.txt": "./vendors~main~ec8c427e.4e43a3f060f64a50053a.bundle.js.LICENSE.txt"
23+
"vendors~main~1f20a385.f0b69229287ff4cc0f0a.bundle.js.LICENSE.txt": "./vendors~main~1f20a385.f0b69229287ff4cc0f0a.bundle.js.LICENSE.txt",
24+
"vendors~main~253ae210.f0b69229287ff4cc0f0a.bundle.js.LICENSE.txt": "./vendors~main~253ae210.f0b69229287ff4cc0f0a.bundle.js.LICENSE.txt",
25+
"vendors~main~678f84af.f0b69229287ff4cc0f0a.bundle.js.LICENSE.txt": "./vendors~main~678f84af.f0b69229287ff4cc0f0a.bundle.js.LICENSE.txt",
26+
"vendors~main~db300d2f.f0b69229287ff4cc0f0a.bundle.js.LICENSE.txt": "./vendors~main~db300d2f.f0b69229287ff4cc0f0a.bundle.js.LICENSE.txt",
27+
"vendors~main~ec8c427e.f0b69229287ff4cc0f0a.bundle.js.LICENSE.txt": "./vendors~main~ec8c427e.f0b69229287ff4cc0f0a.bundle.js.LICENSE.txt"
3028
},
3129
"entrypoints": [
32-
"runtime~main.4e43a3f060f64a50053a.bundle.js",
33-
"vendors~main~253ae210.4e43a3f060f64a50053a.bundle.js",
34-
"vendors~main~d939e436.4e43a3f060f64a50053a.bundle.js",
35-
"vendors~main~db300d2f.4e43a3f060f64a50053a.bundle.js",
36-
"vendors~main~1f20a385.4e43a3f060f64a50053a.bundle.js",
37-
"vendors~main~678f84af.4e43a3f060f64a50053a.bundle.js",
38-
"vendors~main~ec8c427e.4e43a3f060f64a50053a.bundle.js",
39-
"main~24120820.4e43a3f060f64a50053a.bundle.js"
30+
"runtime~main.f0b69229287ff4cc0f0a.bundle.js",
31+
"vendors~main~253ae210.f0b69229287ff4cc0f0a.bundle.js",
32+
"vendors~main~d939e436.f0b69229287ff4cc0f0a.bundle.js",
33+
"vendors~main~db300d2f.f0b69229287ff4cc0f0a.bundle.js",
34+
"vendors~main~1f20a385.f0b69229287ff4cc0f0a.bundle.js",
35+
"vendors~main~678f84af.f0b69229287ff4cc0f0a.bundle.js",
36+
"vendors~main~ec8c427e.f0b69229287ff4cc0f0a.bundle.js",
37+
"main~24120820.f0b69229287ff4cc0f0a.bundle.js"
4038
]
4139
}

docs/iframe.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,4 @@
7171
}</script><style>#root[hidden],
7272
#docs-root[hidden] {
7373
display: none !important;
74-
}</style></head><body><div class="sb-nopreview sb-wrapper"><div class="sb-nopreview_main"><h1 class="sb-nopreview_heading sb-heading">No Preview</h1><p>Sorry, but you either have no stories or none are selected somehow.</p><ul><li>Please check the Storybook config.</li><li>Try reloading the page.</li></ul><p>If the problem persists, check the browser console, or the terminal you've run Storybook from.</p></div></div><div class="sb-errordisplay sb-wrapper"><pre id="error-message" class="sb-heading"></pre><pre class="sb-errordisplay_code"><code id="error-stack"></code></pre></div><div id="root"></div><div id="docs-root"></div><script src="runtime~main.4e43a3f060f64a50053a.bundle.js"></script><script src="vendors~main~253ae210.4e43a3f060f64a50053a.bundle.js"></script><script src="vendors~main~d939e436.4e43a3f060f64a50053a.bundle.js"></script><script src="vendors~main~db300d2f.4e43a3f060f64a50053a.bundle.js"></script><script src="vendors~main~1f20a385.4e43a3f060f64a50053a.bundle.js"></script><script src="vendors~main~678f84af.4e43a3f060f64a50053a.bundle.js"></script><script src="vendors~main~ec8c427e.4e43a3f060f64a50053a.bundle.js"></script><script src="main~24120820.4e43a3f060f64a50053a.bundle.js"></script></body></html>
74+
}</style></head><body><div class="sb-nopreview sb-wrapper"><div class="sb-nopreview_main"><h1 class="sb-nopreview_heading sb-heading">No Preview</h1><p>Sorry, but you either have no stories or none are selected somehow.</p><ul><li>Please check the Storybook config.</li><li>Try reloading the page.</li></ul><p>If the problem persists, check the browser console, or the terminal you've run Storybook from.</p></div></div><div class="sb-errordisplay sb-wrapper"><pre id="error-message" class="sb-heading"></pre><pre class="sb-errordisplay_code"><code id="error-stack"></code></pre></div><div id="root"></div><div id="docs-root"></div><script src="runtime~main.f0b69229287ff4cc0f0a.bundle.js"></script><script src="vendors~main~253ae210.f0b69229287ff4cc0f0a.bundle.js"></script><script src="vendors~main~d939e436.f0b69229287ff4cc0f0a.bundle.js"></script><script src="vendors~main~db300d2f.f0b69229287ff4cc0f0a.bundle.js"></script><script src="vendors~main~1f20a385.f0b69229287ff4cc0f0a.bundle.js"></script><script src="vendors~main~678f84af.f0b69229287ff4cc0f0a.bundle.js"></script><script src="vendors~main~ec8c427e.f0b69229287ff4cc0f0a.bundle.js"></script><script src="main~24120820.f0b69229287ff4cc0f0a.bundle.js"></script></body></html>

0 commit comments

Comments
 (0)