Skip to content

Commit 9af0867

Browse files
authored
feat(web): searchbox bugs and search enhancements (#2178)
* fix: error in console * feat: change debounce value * feat: autosuggest container footer * feat: focus icon button * feat: render shortcut from focusshortcuts and other enhancements * feat: suggestions footer for navigation info * feat: shortname for option key * feat: update reactivecore version * fix(web): sync reactivecore * feat(web): consider theme * chore(web): types * fix(web): focus when clicked * fix(web): styles * fix(web): icon should take precedence over URL
1 parent f884164 commit 9af0867

File tree

7 files changed

+136
-23
lines changed

7 files changed

+136
-23
lines changed

packages/web/examples/DateRangeControlled/src/index.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ class Main extends Component {
1818
}
1919

2020
dateQuery(value) {
21-
console.log('dateQuery', value);
2221
let query = null;
2322
if (value) {
2423
query = [
@@ -42,7 +41,6 @@ class Main extends Component {
4241
}
4342

4443
render() {
45-
console.log('rerender');
4644
return (
4745
<ReactiveBase
4846
app="airbnb-dev"
@@ -55,7 +53,6 @@ class Main extends Component {
5553
value={{ ...this.state.date }}
5654
onChange={(value) => {
5755
// setting end date equal to start date
58-
console.log('value', value);
5956
this.setState({
6057
date: {
6158
start: value.start,

packages/web/src/components/basic/ReactiveBase.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ class ReactiveBase extends Component {
197197
console.error(e);
198198
}
199199

200-
let analyticsRef = null;
200+
let analyticsRef;
201201
if (config.analytics) {
202202
analyticsRef = AppbaseAnalytics.init(analyticsInitConfig);
203203
}

packages/web/src/components/search/SearchBox.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export interface SearchBoxProps extends CommonProps {
3434
highlight?: boolean;
3535
highlightField?: types.stringOrArray;
3636
icon?: types.children;
37+
iconURL?: string;
3738
iconPosition?: types.iconPosition;
3839
includeFields?: Array<string>;
3940
innerClass?: types.style;
@@ -71,6 +72,7 @@ export interface SearchBoxProps extends CommonProps {
7172
distinctField?: string;
7273
distinctFieldConfig?: object;
7374
focusShortcuts?: types.focusShortcuts;
75+
showFocusShortcutsIcon?: boolean;
7476
addonBefore?: types.children;
7577
addonAfter?: types.children;
7678
expandSuggestionsContainer?: boolean;
@@ -82,6 +84,8 @@ export interface SearchBoxProps extends CommonProps {
8284
featuredSuggestionsConfig?: types.featuredSuggestionsConfig;
8385
enableIndexSuggestions?: boolean;
8486
enableFeaturedSuggestions?: boolean;
87+
showSuggestionsFooter?: boolean;
88+
renderSuggestionsFooter?: ()=>types.children;
8589
applyStopwords?: boolean;
8690
customStopwords?: string[];
8791
enterButton?: boolean;

packages/web/src/components/search/SearchBox.js

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ import InputGroup from '../../styles/InputGroup';
4444
import InputWrapper from '../../styles/InputWrapper';
4545
import InputAddon from '../../styles/InputAddon';
4646
import IconGroup from '../../styles/IconGroup';
47-
import IconWrapper from '../../styles/IconWrapper';
47+
import IconWrapper, { ButtonIconWrapper } from '../../styles/IconWrapper';
4848
import SearchSvg from '../shared/SearchSvg';
4949
import { TagItem, TagsContainer } from '../../styles/Tags';
5050
import Container from '../../styles/Container';
@@ -66,6 +66,7 @@ import CustomSvg from '../shared/CustomSvg';
6666
import SuggestionWrapper from './addons/SuggestionWrapper';
6767
import AutofillSvg from '../shared/AutofillSvg';
6868
import Flex from '../../styles/Flex';
69+
import AutosuggestFooterContainer from '../../styles/AutoSuggestFooterContainer';
6970

7071
const useConstructor = (callBack = () => {}) => {
7172
const [hasBeenCalled, setHasBeenCalled] = useState(false);
@@ -94,6 +95,8 @@ const SearchBox = (props) => {
9495
URLParams,
9596
customQuery,
9697
customEvents,
98+
showSuggestionsFooter,
99+
renderSuggestionsFooter,
97100
} = props;
98101

99102
const internalComponent = getInternalComponentID(componentId);
@@ -672,6 +675,14 @@ const SearchBox = (props) => {
672675
}
673676
return null;
674677
};
678+
const handleFocus = (event) => {
679+
if (props.autosuggest) {
680+
setIsOpen(true);
681+
}
682+
if (props.onFocus) {
683+
props.onFocus(event, triggerQuery);
684+
}
685+
};
675686

676687
const getComponent = (downshiftProps = {}) => {
677688
const { error, isLoading, rawData } = props;
@@ -741,7 +752,17 @@ const SearchBox = (props) => {
741752

742753
const renderIcon = () => {
743754
if (props.showIcon) {
744-
return props.icon || <SearchSvg />;
755+
if (props.icon) {
756+
return props.icon;
757+
}
758+
if (props.iconURL) {
759+
return (<img
760+
style={{ maxHeight: '25px' }}
761+
src={XSS(props.iconURL)}
762+
alt="search-icon"
763+
/>);
764+
}
765+
return <SearchSvg />;
745766
}
746767
return null;
747768
};
@@ -752,6 +773,18 @@ const SearchBox = (props) => {
752773
}
753774
return null;
754775
};
776+
const renderShortcut = () => {
777+
if (props.focusShortcuts && props.focusShortcuts.length) {
778+
let shortcut = props.focusShortcuts[0];
779+
shortcut = shortcut.toLowerCase();
780+
shortcut = shortcut.replace('shift', '⬆️');
781+
shortcut = shortcut.replace('command', 'cmd');
782+
shortcut = shortcut.replace('control', 'ctrl');
783+
shortcut = shortcut.replace('option', 'alt');
784+
return shortcut.toUpperCase();
785+
}
786+
return '/';
787+
};
755788

756789
const renderIcons = () => {
757790
const {
@@ -762,6 +795,7 @@ const SearchBox = (props) => {
762795
showVoiceSearch,
763796
iconPosition,
764797
innerClass,
798+
showFocusShortcutsIcon,
765799
} = props;
766800
return (
767801
<div>
@@ -771,6 +805,14 @@ const SearchBox = (props) => {
771805
{renderCancelIcon()}
772806
</IconWrapper>
773807
)}
808+
{
809+
showFocusShortcutsIcon
810+
&& (
811+
<ButtonIconWrapper onClick={e => focusSearchBox(e)}>
812+
{renderShortcut()}
813+
</ButtonIconWrapper>
814+
)
815+
}
774816
{shouldMicRender(showVoiceSearch) && (
775817
<Mic
776818
getInstance={getMicInstance}
@@ -789,18 +831,20 @@ const SearchBox = (props) => {
789831
<IconWrapper onClick={handleSearchIconClick}>{renderIcon()}</IconWrapper>
790832
)}
791833
</IconGroup>
834+
792835
</div>
793836
);
794837
};
838+
const SuggestionsFooter = () => (
839+
typeof renderSuggestionsFooter === 'function'
840+
? renderSuggestionsFooter()
841+
: ((
842+
<AutosuggestFooterContainer>
843+
<div>↑↓ Navigate</div>
844+
<div>↩ Go</div>
845+
</AutosuggestFooterContainer>))
846+
);
795847

796-
const handleFocus = (event) => {
797-
if (props.autosuggest) {
798-
setIsOpen(true);
799-
}
800-
if (props.onFocus) {
801-
props.onFocus(event, triggerQuery);
802-
}
803-
};
804848

805849
const onAutofillClick = (suggestion) => {
806850
const { value } = suggestion;
@@ -1337,6 +1381,9 @@ const SearchBox = (props) => {
13371381
</li>
13381382
);
13391383
})}
1384+
1385+
{showSuggestionsFooter
1386+
? <SuggestionsFooter /> : null}
13401387
</ul>
13411388
) : (
13421389
renderNoSuggestion(parsedSuggestions())
@@ -1443,6 +1490,7 @@ const SearchBox = (props) => {
14431490
/>
14441491
{renderIcons()}
14451492
</InputWrapper>
1493+
14461494
{renderInputAddonAfter()}
14471495
{renderEnterButtonElement()}
14481496
</InputGroup>
@@ -1496,6 +1544,7 @@ SearchBox.propTypes = {
14961544
highlight: types.bool,
14971545
highlightField: types.stringOrArray,
14981546
icon: types.children,
1547+
iconURL: types.string,
14991548
iconPosition: types.iconPosition,
15001549
innerClass: types.style,
15011550
includeFields: types.includeFields,
@@ -1538,11 +1587,14 @@ SearchBox.propTypes = {
15381587
renderMic: types.func,
15391588
//
15401589
focusShortcuts: types.focusShortcuts,
1590+
showFocusShortcutsIcon: types.bool,
15411591
addonBefore: types.children,
15421592
addonAfter: types.children,
15431593
expandSuggestionsContainer: types.bool,
15441594
popularSuggestionsConfig: types.componentObject,
15451595
recentSuggestionsConfig: types.componentObject,
1596+
showSuggestionsFooter: types.bool,
1597+
renderSuggestionsFooter: types.func,
15461598
applyStopwords: types.bool,
15471599
customStopwords: types.stringArray,
15481600
onData: types.func,
@@ -1565,7 +1617,7 @@ SearchBox.propTypes = {
15651617
SearchBox.defaultProps = {
15661618
autosuggest: true,
15671619
className: null,
1568-
debounce: 0,
1620+
debounce: 100,
15691621
downShiftProps: {},
15701622
enableSynonyms: true,
15711623
enablePopularSuggestions: false,
@@ -1576,11 +1628,13 @@ SearchBox.defaultProps = {
15761628
queryFormat: 'or',
15771629
showFilter: true,
15781630
showIcon: true,
1631+
showFocusShortcutsIcon: true,
15791632
showVoiceSearch: false,
15801633
style: {},
15811634
URLParams: false,
15821635
showClear: false,
15831636
showDistinctSuggestions: true,
1637+
showSuggestionsFooter: true,
15841638
strictSelection: false,
15851639
searchOperators: false,
15861640
size: 10,
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import styled from '@emotion/styled';
2+
3+
const AutosuggestFooterContainer = styled.div`
4+
display: flex;
5+
justify-content: space-between;
6+
align-items: center;
7+
color: #8792a2;
8+
background: #f7fafc;
9+
padding: 10px;
10+
height: 40px;
11+
`;
12+
13+
export default AutosuggestFooterContainer;

packages/web/src/styles/IconWrapper.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,15 @@ const IconWrapper = styled.div`
4040
}
4141
`;
4242

43+
export const ButtonIconWrapper = styled(IconWrapper)`
44+
border-radius: 4px;
45+
vertical-align: middle;
46+
height: 25px;
47+
font-size: 12px;
48+
border: 1px solid ${({ theme }) => (theme.colors ? theme.colors.primaryColor : '#000')};
49+
color: ${({ theme }) => (theme.colors ? theme.colors.primaryColor : '#000')};
50+
max-width: unset;
51+
padding: 5px;
52+
`;
53+
4354
export default IconWrapper;

0 commit comments

Comments
 (0)