reactstrap, dropdown with search input - reactjs

In order to benefit the design of reactstrap dropdown, I want to use it as a search bar with results shown in the dropdown menu. But the default key listener that enables navigating through results by keyboard (arrow keys Up/Down), only captured by Input, and cannot propagate it to the parent or whatever is listening to key events when the result is visible.
<Dropdown toggle={() => setIsOpen(!isOpen)} isOpen={isOpen}>
<DropdownToggle>
<Input onChange={(e) => setQuery(e.target.value)} placeholder="search placeholder" />
</DropdownToggle>
<DropdownMenu>
{fetchedItems.map((item) => (
<div key={item}>
<DropdownItem>action #1</DropdownItem>
<DropdownItem>action #1</DropdownItem>
<DropdownItem text>Dropdown Item Text</DropdownItem>
<DropdownItem disabled>Action (disabled)</DropdownItem>
<DropdownItem divider />
</div>
))}
</DropdownMenu>
</Dropdown>
Now looking at this sandbox I am searching for two approaches, either add key event listeners to default as default dropdown behavior, or customize the <Input type=" search"/>
Now the question is how to do it. I assume handling key listener might be better.

One way to do this would be by adding an eventListener with the dropdown component.
You would also want to maintain a state which tracks the currently navigated dropdown item's index.
The eventListener can increment and decrement the selected index
If nothing is selected, on up keystroke set 0 and on down keystroke set length - 1 as the current index
Now increment or decrement on different keystrokes
If something new is searched, change it to undefined or 0 at your convenience
Give custom styling to the currently navigated index

Please follow following thing:
1)The above "event listner" should be part of parent of list.
2)Set value of "current-index" to 0 once you get new set of data.
3) When user move the selection with the arrow button "incsease" and "decrease" once as per "key-value".
4)If use jump from one point to other get the "id" or "serial number".
5) Ideal event listener should be "onfoucs" if using on the children.

The whole problem was from a type of event listener, so the DropDownToggle component receives the onKeyDown events not the onKeyPress
<DropdownToggle onKeyDown={(e) => {/* now have access to e.key */} }

Related

React event order

Full reproduction: codesandbox link .
I am trying to create a custom search/select component with react. My problem lies in execution order of events.
When a user focuses in on the text input field, a list with options should be shown. Then when the user clicks on one of the items in the dropdown, the element name should be filled in the text input field and then the list should be hidden.
I achive this by creating a inputFocused boolean state, and rendering list only when inputFocused is true. I attach twop event handlers on the text input itself:
onFocus={() => setInputFocused(true)}
onBlur={() => setInputFocused(false)}
But when a user clicks on one of the items, input is not getting filled.
<input
value={searchTerm}
onChange={(e) => {
setSearchTerm(e.target.value);
}}
onBlur={() => setInputFocused(false)}
onFocus={() => setInputFocused(true)}
/>
{inputFocused && (
<ul>
<li onClick={() => setSearchTerm(element.name)}>{element.name}</li>
</ul>
}
Check codesanbox link for better understanding.
I think i understand the problem. onBlur() event triggers before the onClick() event. If i wrap the onBlur callback function inside a setTimout, it will work as expected:
onBlur={() =>
setTimeout(() => {
setInputFocused(false);
}, 100)
}
I would like to avoid defining a setTimeout.
Is there solution to make sure that onClick() on <li> executes before onBlur() on <input /> ?
You can change onClick to onMouseDown on the list items, and the ordering will be handled properly by the browser.
The events get fired in the order of mousedown -> mouseup -> click (see
https://javascript.info/mouse-events-basics#events-order)

React Semantic-UI: Dropdown.Menu / Problem

I am trying to build a language selector dropdown showing the flag symbols of the languages - in the menu when it is not opened as well as in the opened menu. I would not like to show any texts.
I am using inline style to style my components in my app, therefore I would like to define the menu style as well via style={myStyle}, else the opening dropdown menu won't be affected by the style in the component.
<Dropdown
value={value}
selection
compact
style={myStyle}
onChange={getLanguage} // could be removed
options={countryOptions}
>
<Dropdown.Menu style={myStyle} >
{countryOptions.map( country => {
return (
<Dropdown.Item key={country.key} value={country.value} flag={country.flag} onClick={getLanguage} />
)
})}
</Dropdown.Menu>
</Dropdown>
I use the code above and it works for me, the only problem is, that I get the error message, that I cannot use <Dropdown.Items> and options in the component at the same time:
Warning: Failed prop type: Prop `selection` in `Dropdown` conflicts with props: `children`. They cannot be defined together, choose one or the other.
My problem without using the options is, that I don't know how to set the active item in the component. Without the options it seems, it is not possible to display the current value inside this component.
What am I missing or doing wrong? Is it possible at all to set the current value without using options? Can I ignore the error message?
Thank you a lot for your help.
I think what you're asking for is to set active prop on the Dropdown.Item, with a test to see if the Item value matches the set value for the dropdown.
<Dropdown
value={value}
compact
style={myStyle}
selection
onChange={getLanguage} // could be removed
options={countryOptions}
>
<Dropdown.Menu style={myStyle} >
{countryOptions.map( country => {
return (
<Dropdown.Item
key={country.key}
value={country.value}
active={(value === country.value)}
flag={country.flag}
onClick={getLanguage}
/>)
})}
</Dropdown.Menu>
</Dropdown>

Why would a button's value field by occasionally missing in an event callback

Given an array of data, I use the first row to generate the headings, including a pair of buttons like this:
Object.keys(data[0]).map((key, i) => (
<Button size="sm" variant="link"
value={key} onClick={(event) => console.log(event.target.value)}>
sort asc
</Button>
The issue is that about a random half(ish) of clicks, event.target.value is undefined.
There does not appear to be a pattern, but every 2nd, or 3rd click or so will find the value missing.
What am I missing?
This is react.

React widgets combobox; how to clear input or prevent selection

I'm using the combobox from React Widgets as a search UI component.
I've put in a custom render item so that when you click a search result in the dropdown, you navigate to the relevant page.
However when you select a result, the name of the selected item goes into the text input, which isn't what a user will expect when they select a search result. I think they'd expect the search term to remain, or perhaps the input to be cleared.
I like the Combobox component and haven't found another UI widget that would do what I want, so I'd like to find a solution.
Is there some way to override the selection behaviour so that clicking a list item doesn't select it? I've tried setting the 'onSelect' property but this doesn't suppress the default selection behaviour, it just adds extra functionality.
Alternatively is there a way to manually set the selection to null? The docs don't seem to show anything. I tried getting the input node's value manually to '' with reactDOM, but the value didn't change. I would guess that the component controls it.
I've wrapped the Combobox in a functional component:
function Search(props) {
...
const onSelect = (value) => {
const node = ReactDOM.findDOMNode(Search._combobox);
const input = node.getElementsByTagName('input')[0];
input.value = '';
}
return (
<Combobox
ref={(c) => Search._combobox = c}
onSelect={onSelect}
textField="name"
valueField="_id"
/>
);
}
If I set the value prop of the Combobox then it is impossible to type into it.
Any suggestions? Thank you.
The solution I found is to create my own search controls using an input and a button, and hide the native input and button with display: none. "componentDidUpdate" detects when new search results arrive and opens the dropdown to show them.
There is a manually-added 'show more...' entry at the end of search results. Clicking this increases the search limit for that group. That's the main reason I wanted to avoid showing the clicked result in the text input. The custom input is not affected by the user's selection, it always shows the search term.
My search component now looks something like this:
<div className="search">
<div className="search-controls">
<Input
onChange={this.onChangeInput}
type="text"
/>
<Button
onClick={this.toggleOpen}
title="toggle results"
>
<FontAwesomeIcon icon={['fas', 'search']} style={{ 'color': iconColors.default }} size="1x" />
</Button>
</div>
<Combobox
busy={isSearching}
data={searchResults}
onChange={() => {}}
open={open}
onSelect={this.onSelect}
textField="name"
valueField="_id"
/>
</div>

Dynamic Popover in ReactJS

I'm fairly new to React and I'm using the Ant Design framework (https://ant.design/).
I have a table list of items that I'm looking to have a button on each so that when it is pressed additional information about that row becomes available (which is a secondary API call specific to that row).
I'm trying to use a popover but I'm noticing that the popover wants the text before being rendered which is a problem since I don't have that information until the second API call. The best idea I've come up so far is to have the button press trigger the api call and then the state is updated but that creates a funky experience (as it is update after the popover is already opened - after starting with the previous rows information). It isn't a huge amount of time but it still isn't an ideal experience.
This is what I have so far:
<Popover content={this.contentSec([record['section']])} title=
{record['section']} trigger="click">
<Button onClick={() => this.sectionAttributes(record['section'])}>
<Icon type="info-circle-o" />
</Button>
</Popover>
this.sectionAttributes triggers my fetch request. and this.contentSec does the formatting on the existing popup (see below).
contentSec(props) {
const listItems = this.state.attributes.map((item) =>
<li>{item}</li>
);
return <div><ul>{listItems}</ul></div>
}
Any ideas of a better way to handle this so that there isn't that buggy delay when clicking the button for the popover?
Thanks.
As the content is populated after the second api call, You can send two props content which can be empty string or any default value and loading variable as true on click. Once you have the data after the API call you can send the updated props ie content with actual text and loading as false and finally handle your logic and state update in componentwillrecieveprops() in the popover component.
You can use the loading prop to switch from a loader initially to the actual content.
Write a condition to check if the response from this.sectionAttributes(record['section'] is true, if it is true then
<Popover content={this.contentSec([record['section']])} title=
{record['section']} trigger="click">
<Button onClick={() => this.sectionAttributes(record['section'])}>
<Icon type="info-circle-o" />
</Button>
</Popover>
else simply use
<Button onClick={() => this.sectionAttributes(record['section'])}>
<Icon type="info-circle-o" />
</Button>
For the first time user clicks on button, the component rerenders and state gets updated, so when it rerenders you can see popover as the if condition satisfy.

Resources