How to capture when dropdown is open on React - javascript

Is there a way to tell when the dropdown is open and also closed? onfocus and onblur doesn't seem to be working.
<div className="select-container">
<select>
{options.map((option) => (
<option value={option.value}>{option.label}</option>
))}
</select>
</div

You should use useState to keep track of the dropdown status. It would look something like this:
import "./styles.css";
import { useState } from "react";
export default function App() {
const [isDropDownOpen, setDropDownOpen] = useState(false);
let options = [
{
label: "money"
}
];
const handleSelect = () => {
setDropDownOpen(!isDropDownOpen);
};
const handleBlur = () => {
setDropDownOpen(!isDropDownOpen);
};
console.log(isDropDownOpen);
return (
<div>
<select onBlur={handleBlur} onClick={handleSelect}>
{options.map((option) => (
<option value={option.value}>{option.label}</option>
))}
</select>
</div>
);
}
I have tied it into the handleSelect function, which will probably do more than just keep track of whether or not the dropdown is open but either way, it works as a reference point for now.
EDIT: Also, in case you click outside the dropdown, I used onBlur, which is controlled by handleBlur to change the boolean value because obviously, the dropdown will close.
Check the console.log on the this code sandbox to see how it works: https://codesandbox.io/s/amazing-easley-0mf7o3?file=/src/App.js

Related

Can I track multiple checkboxes with React Hooks or do I need to use a Class component?

I'm learning React Hooks and trying to understand how I can manage multiple checkboxes with state. If I only have one checkbox, my code below works - using check as state - but when I have multiple boxes, this doesn't work because onChange = {() => setCheck(!check)} will change the check state for ALL of the boxes at once.
I think it's doable if I use a React class component (something like this.handleCheckboxChange to only change state for the particular checkbox) but I'm trying to see if it's possible to do this with hooks.
import React, { useState } from 'react';
const Search = ({ options }) => {
const [check, setCheck] = useState(false);
const renderedOptions = options.map((option) => {
return (
<div key={option}>
<label>
<input
checked={check ? 'checked' : ''}
type="checkbox"
name={option}
value={option}
onChange={() => {
setCheck(!check);
}}></input>
{option}
</label>
</div>
);
});
return (
<form>
<label>Search Engines (check all that apply)</label>
{renderedOptions}
</form>
);
};
export default Search;
You can use an object as the state, with each propname as the name of the checkbox and can hold the checked information as value.
const [values, setValues] = useState({});
handleChange = (e) => setValues({ ...values, [e.target.name]: e.target.checked });
You can bind the options as initial or default values to the state. And <input value={values[options.name]

antd select option with search functionality

i dont know if this is possible but at the moment when user click from select options it will take to that specific url, my point is can we have search functionality in 'Search...' so user could search from hundreds of option and then choose it and click the button and that button should also take to that same 'onChange' address. You can also suggest other options, even without select but functionality must be same , here is my code:
import "antd/dist/antd.css";
import { Select } from "antd";
// import { Link, useHistory, useRouteMatch, useParams } from "react-router-dom";
function EventsSection() {
// const history = useHistory();
const { Option } = Select;
// const changeHandler = (id) => {
// history.push(`/customer/${id}`);
// };
return (
<div>
{/* when found in search i want this button take to 'onChange' address also*/}
<button>click me when found in search</button>
<Select
style={{ width: 200 }}
placeholder="Search..."
mode="multiple"
open={true}
listHeight={128}
// onChange={changeHandler}
>
<Option value="1">Not Identified</Option>
<Option value="2">Closed</Option>
<Option value="3">Communicated</Option>
<Option value="4">Identified</Option>
<Option value="5">Resolved</Option>
<Option value="6">Cancelled</Option>
</Select>
</div>
);
}
export default EventsSection;
The first part is done.
Check at CodeSanbox.
The second part is still unclear to me.
Exactly this part
and then that button can be cliked which should take user to that
’onChange’

Clear datalist input onClick in React controlled component

I have a html5 input with an associated datalist inside a React controlled component. I want to clear the text when the input field is clicked or receives focus so all options are displayed for selection. I've followed Alfred's excellent answer in this question but am unable to achieve quite the same result in a React controlled component. Unfortunately, calling blur inside the onClick handler prevents my users from typing more than a single character because focus is (of course) lost.
How can I maintain the ability for users to type but clear the text and show the full set of options whenever the text box is clicked?
import React, { useState } from "react";
const MyForm = () => {
const [options, setOptions] = useState(["Apples", "Oranges", "Bananas", "Grapes"]);
const handleChange = (event) => {
event.target.blur();
};
const clear = (event) => {
event.target.value = "";
};
return (
<>
<input
type="input"
list="optionsList"
onChange={handleChange}
onFocus={clear}
placeholder="Select an option"
/>
<datalist id="optionsList">
{options.map((o) => (
<option key={o}>{o}</option>
))}
</datalist>
</>
);
};
export default MyForm;
Note that I've also tried a version of this that calls clear onClick rather than onFocus. That keeps me from needing to call blur() in handleChanges so the problem typing is solved. But, this requires that I click twice to see the full set of options because the list of options seems to be presented before the box is cleared.
Saw your comment on one of my question, so I figured I'd post it here as an answer instead.
Based on your use case, here is what I think you will need
import React, { useState } from "react";
const MyForm = () => {
const [options, setOptions] = useState(["Apples", "Oranges", "Bananas", "Grapes"]);
const handleChange = (event) => {
if (!event.nativeEvent.inputType) {
event.target.blur();
}
};
const clear = (event) => {
event.target.value = "";
};
return (
<>
<input
type="input"
list="optionsList"
onChange={handleChange}
onClick={clear}
onFocus={clear}
placeholder="Select an option"
/>
<datalist id="optionsList">
{options.map((o) => (
<option key={o}>{o}</option>
))}
</datalist>
</>
);
};
export default MyForm;
In order to prevent handleChange from blocking text input normally, you will have to check for event.nativeEvent.inputType, as onChange triggered by clicking on datalist will not have an inputType value. So in this case we will only perform the input blur when it is populated by datalist and keep the focus for any other events.
I have also added an additional onClick handler to clear the input regardless whether the input is already in focus or not.
I guess you actually want to have input value as a state, and not the options.
Therefore possible controlled component implementation should be:
const options = ["Apples", "Oranges", "Bananas", "Grapes"];
const EMPTY_INPUT = "";
const MyForm = () => {
const [value, setValue] = useState(EMPTY_INPUT);
const onFocusClear = () => {
setValue(EMPTY_INPUT);
};
const onChange = ({ target: { value } }) => {
setValue(value);
};
return (
<>
<input
value={value}
type="input"
list="optionsList"
onChange={onChange}
onFocus={onFocusClear}
placeholder="Select an option"
/>
<datalist id="optionsList">
{options.map((o) => (
<option key={o}>{o}</option>
))}
</datalist>
Value: {value}
</>
);
};
And making it an uncontrolled component is pretty simple by removing the onChange. Now you have the input value in ref.current.value (Not so useful use case, just an example).
const MyForm = () => {
const inputRef = useRef();
const onFocusClear = () => {
inputRef.current.value = ''
};
return (
<>
<input
type="input"
list="optionsList"
onFocus={onFocusClear}
placeholder="Select an option"
/>
<datalist id="optionsList">
{options.map((o) => (
<option key={o}>{o}</option>
))}
</datalist>
</>
);
};

React event listeners with useEffect

import React, { useState, useEffect } from "react";
import "./styles.css";
export default function App() {
const [theRoom, setRoom] = useState(null);
const updateVideoDevice = (e) => {
console.log("room", theRoom);
};
const createRoom = () => {
console.log("we change the room", theRoom);
setRoom({
localparticipants: {}
});
console.log("we change the room after", theRoom);
};
useEffect(() => {
const select = document.getElementById("video-devices");
select.addEventListener("change", updateVideoDevice);
}, []);
return (
<div className="App">
<select id="video-devices">
<option value="1">1</option>
<option value="2">2</option>
</select>
<button onClick={createRoom}>change obj</button>
</div>
);
}
I have this codebase. When I press the change obj button for the first time it doesn't set theRoom to the object
{
localparticipants: {}
}
But when I press the button for the second time it does, and after that, I try to change the select element's options I got null for the console log in the updateVideoDevice function.
How do I solve these two issues with React?
const [theRoom, setRoom] = useState(null);
setRoom is asynchronous function, so you will not see the change immediately.
Also, you don't need to add event listener to select tag. You can simply use onChange method. So remove all with useEffect and then change the code as below.
<select id="video-devices" onChange={updateVideoDevice}>
you need to chnage how you call the onclick method so it's look like this:
<button onClick={()=>createRoom()}>change obj</button>
Here's a working solution:
setRoom is asynchronous so your 2 console.log(theRoom) can't show the right value while you're still in the function.
to handle the select change value, you can use the onChange React prop that allows you to catch every value change of a hook
I put you 2 console.login a useEffect to allow you to see the changes of the theRoom and selectValue values
import React, { useState, useEffect } from "react";
import "./styles.css";
export default function App() {
const [theRoom, setRoom] = useState(null);
const [selectValue, setSelectValue] = useState(null);
const createRoom = () => {
setRoom({
localparticipants: {}
});
};
// everytime selectValue or theRoom values changed, this trigger this
useEffect(() => {
console.log(selectValue);
console.log(theRoom);
}, [selectValue, theRoom]);
return (
<div className="App">
<select
id="video-devices"
onChange={(e) => setSelectValue(e.target.value)}
value={selectValue}
>
<option value="1">1</option>
<option value="2">2</option>
</select>
<button onClick={createRoom}>change obj</button>
</div>
);
}

React testing: Change select value in test

I am trying to write a test to check a value after a change in a <select> dropdown. This is my component:
import React from 'react';
const BasicDropdown = ({ key, value, options }) => {
return (
<div className={`basic-dropdown-${key}`}>
<select
className={`basic-dropdown-${key}-select`}
name={`basic-dropdown-${key}-select`}
{...{value}}
>
{options.map(option =>
<option
className={`basic-dropdown-${key}-select-${option}-option`}
key={option}
value={option}
>
{option.charAt(0).toUpperCase() + option.slice(1)}
</option>
)}
</select>
</div>
);
};
export default BasicDropdown;
So far it's very simple. The reason for having a component is that I will expand this later depending on the props and other things. So I decided to write a test for this component to start with:
import React from 'react'
import TestUtils from 'react-dom/test-utils';
import BasicDropdown from './BasicDropdown';
const options = ['option-A', 'option-B', 'option-C'];
describe("BasicDropdown", () => {
let renderedCmpt;
beforeAll(() => {
renderedCmpt = TestUtils.renderIntoDocument(
<BasicDropdown key="test" value="option-A" options={options} />
)
});
it("Should have correct value after change", () => {
const select = TestUtils.findRenderedDOMComponentWithClass(renderedCmpt, 'basic-dropdown-test-select');
expect(select.value).toEqual("option-A");
TestUtils.Simulate.change(select, {target: {value: 'option-C'}});
const selectChanged = TestUtils.findRenderedDOMComponentWithClass(renderedCmpt, 'basic-dropdown-test-select');
expect(selectChanged.value).toEqual("option-C");
});
});
My problem is that when running this test using jest (jest --coverage=false --config ~/projects/Beehive/config/jest.config.js ~/projects/Beehive/tests/BasicDropdown-test.js) I get the following error:
Expected: "option-C"
Received: "option-A"
at line 21, which means that the value of the select is never changed during the Simulate.
What am I doing wrong?
You need to add onChange on select element to reflect the change on the test, else it will always be option-A.

Categories

Resources