How to stop the toggle/show item in react-bootstrap Dropdown? - reactjs

code:-
<td style={{ color: "white", width: "250px" }}>
<DropdownButton id="SelectFocus" title={playdata.ChannelName} tabIndex={-1}
onToggle={ontoggle}
menuVariant='dark'
>
{Channelname.map((val, id) => {
{
return (
<Fragment key={id}>
{removeRedundant([...val, playdata.ChannelName]).map((val1) => {
return (
<Dropdown.Item
onClick={(e) => { setPlayer(val1, playdata.idx, StoreIdx) }}
key={val1}>{val1}</Dropdown.Item>
)
})}
</Fragment>
)
}
})
}
</DropdownButton>
</td>
I am using react bootstrap for dropdown when I press down key button its automatically open the items/options how can I stop that?
i don't want item appear when I press the down key so how can I stop that in react js?
Please help...

When you use DropdownButton component, there is an additional div around the button. Use more low level Dropdown components to set tabIndex on button directly:
<Dropdown>
<Dropdown.Toggle tabIndex={-1}>Test</Dropdown.Toggle>
<Dropdown.Menu>
<Dropdown.Item>Test 1</Dropdown.Item>
<Dropdown.Item>Test 2</Dropdown.Item>
<Dropdown.Item>Test 3</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
But keep in mind once you have focused the dropdown with click, you will get the same behaviour when pressing arrow down.
To prevent dropdown from toggling I believe you can use the onKeyDown event, but I wasn't able to get it work so far:
const handleKeyDown = (event) => {
if (event.key === "ArrowDown") {
event.preventDefault();
}
};

Related

React Mui Autocomplete resets scroll after selecting values

So I'm trying to set up a mui-autocomplete component with additional buttons (Clear all (clear all values and close dropdown) + Apply (set value and close dropdown)) using ListboxComponent.
Issues:
when selecting options from the bottom of the list, the scroll position is reset to the top
cannot close the dropdown programmatically
Here is the ListboxComponent
ListboxComponent={(listBoxProps) => {
return (
<div>
<ul {...listBoxProps} />
<div>
<button
onMouseDown={(event) => {
// Disable blur
event.preventDefault();
}}
onClick={() => {
// clear values
setSelected([]);
}}
>
Clear All
</button>
<button
onMouseDown={(event) => {
// Disable blur
event.preventDefault();
}}
onClick={() => {
// apply value
}}
>
Apply
</button>
</div>
</div>
);
The options are rendered as follows:
renderOption={(optionProps, option, optionState) => {
return (
<li {...optionProps}>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
checked={optionState.selected}
/>
{option}
</li>
);
}}
So I'm using state to keep track of saving the selected values:
const [selectedResult, setSelected] = useState([]);
And when the option is selected - the state is updated
onChange={(event, selectedOptions) => {
setSelected(selectedOptions);
}}
But when the state changes, the component is re-rendered and the scroll is reset. It also seems that I can't use local variables to store the intermediate result, as the state won't update and the checkbox won't update.
StackBlitz link
Is there anything I can do to achieve this?

Accessible dynamically created unordered list

I am currently tasked with making a current dynamic search accessible. Currently the search field all seems to work and announce. However when triggered the list propagates but does not seem to interact with the screen reader. I did not create this component just tasked with making it a11y compatible.
For example if I type the letter "d" in the search field the list shows all elements that match that. But on arrowDown the screen reader does not announce the value to select. I am curious if there is something I am missing here to allow that to happen, or perhaps my screen reader is just not as advanced as others.
The code is in React and as follows:
<div id={id} className={classNames.join(" ")} onClick={focusInput}>
<a
className="search-icon"
onClick={(ev) => {
onClickLookupIcon();
ev.stopPropagation();
}}
/>
<div className={"styled-scrollbar " + itemsClassNames.join(" ")}>{itemEls}</div>
{results.length > 0 && (
<ul className="auto-complete" style={{ top: allowMultiple ? "42px" : "22px" }}>
{results.map((it, i) => (
<li
key={i}
className={i === selectedSearchIndex ? "selected" : ""}
//use mousedown so that this triggers before the "blur" event on the input which would trigger selecting the `selectedSearchIndex` item instead of the clicked item
onMouseDown={(ev) => {
addOrReplaceItem({ type: "valid", value: it });
resetLookup();
ev.stopPropagation();
}}
>
<SearchDelegate {...it} />
</li>
))}
</ul>
)}
</div>
Expanding some code to see more re {itemEls}
const itemEls = value.map((it) => {
const itemId = it.type === "valid" ? getRecordId(it.value) : it.newItemId;
return currEditingRecord === itemId ? (
inputEl
) : (
<div
key={itemId}
className="hg-lookup-item-wrapper"
onClick={(ev) => {
ev.stopPropagation();
//edit the clicked-on item unless we're already editing it
if (currEditingRecord === itemId) return;
setSearchString(it.type === "valid" ? getItemSearchText(it.value) : it.searchString);
setCurrEditingRecord(itemId);
//select all text when clicking on existing item
setTimeout(() => inputRef.current && inputRef.current.select());
}}
>
{it.type === "valid" ? (
<>
<Delegate item={it.value} />
<a className="hg-remove-icon" onClick={doRemove(itemId)} />
</>
) : (
<PendingResult<T>
key={it.newItemId}
loadMatches={loadMatches}
searchString={it.searchString}
onValueSelected={(val) => {
onChange(
value.map((v) =>
v.type === "valid" || v.newItemId !== it.newItemId || valueExists(val)
? v
: { type: "valid", value: val },
),
);
}}
/>
)}
</div>
);
});
and lastly the inputEl
const inputEl = (
<div key="INPUT" className="hg-lookup-item-wrapper">
<AutosizeInput
id={`${id}-input`}
type="text"
ref={inputRef}
aria-autocomplete="list"
aria-controls={`${id}-listbox`}
value={searchString || ""}
onBlur={() => {
takeSelectedResultOrSetupPendingResult(false);
resetLookup();
}}
autoComplete={"off"}
{...{ disabled }}
onChange={(ev) => {
if (!allowMultiple && isAddingNewRecord() && value.length === 1) return;
setSearchString(ev.currentTarget.value);
}}
onKeyDown={onKeyDown}
/>
</div>
);
on mouseDown the screen reader does not announce the value to select
So you're using a mouse in addition to a screen reader? While there are some users that have that combination, the majority of screen reader users use only the keyboard and not a mouse. Does your component work with just a keyboard? Can you tab to it and the label for the component is read (WCAG 4.1.2). If you type d and a list appears below it, can you arrow down to an item and press enter to select it (WCAG 2.1.1)?
I would initially focus on making sure everything works from a keyboard first, and then augment that work to ensure things are announced by a screen reader. For example, typing d shows a list of options. Are the number of items in the list announced?
While your object might not be a combobox, you should look at the combobox pattern, especially the "autocomplete" options.
https://www.w3.org/TR/wai-aria-practices/#combobox
And check out example 1 on:
https://www.w3.org/TR/wai-aria-practices/examples/combobox/aria1.1pattern/listbox-combo.html#ex1_label

Autoclose='outside' doesn't work with multiple menu in react-bootstrap

I have a dropdown with 2 possibles menus that can reach each others by clicking on some dropdown items. By adding autoClose='outside' I fixed the problem of untimely closing when clicking somewhere in the dropdown main menu. But since I have another menu, once it's showed, the first time I open it, the autoClose doesn't really work, instead, it's as if it was never set, close the whole dropdown, and when I reopen it, go in the secondary menu for the second, this 'bug' doesn't occur again on this menu, but occurs when I go back in the first one etc..
I suspect it's because autoClose only works on the current selected menu, in the example below it works with the main menu and does not in the secondary menu first then, as described above, when I reopen the dropdown it shows directly the secondary menu, works, and once I go back in the main menu, it doesn't.
This is basically the code I'm running.
import {Dropdown} from 'react-bootstrap';
import {useState} from 'react';
const Mydropdown = (props) => {
const [menu,setMenu] = useState('main');
return(
<>
<Dropdown autoClose='outside'>
{
menu=="main"
&&
(
<>
<Dropdown.Menu>
<Dropdown.Item onClick={()=>setMenu("secondary")}>
Secondary menu
</Dropdown.Item>
</Dropdown.Menu>
</>
)
}
{
menu=="secondary"
&&
(
<>
<Dropdown.Menu>
<Dropdown.Item onClick={()=>setMenu("main")}>
Secondary menu
</Dropdown.Item>
</Dropdown.Menu>
</>
)
}
</Dropdown>
</>
);
}
I'm not sure I'm following your requirements 100%, but this version should allow a single dropdown to optionally toggle contents (without auto-closing):
const Mydropdown = (props) => {
const [menu, setMenu] = useState("main");
return (
<Dropdown autoClose="outside">
<Dropdown.Toggle variant="primary">Dropdown Test</Dropdown.Toggle>
<Dropdown.Menu>
<Dropdown.Item href="#">Menu Item</Dropdown.Item>
<Dropdown.Item href="#">Menu Item</Dropdown.Item>
<Dropdown.Item href="#">Menu Item</Dropdown.Item>
{menu == "main" && (
<Dropdown.Item onClick={() => setMenu("secondary")}>
Secondary menu
</Dropdown.Item>
)}
{menu == "secondary" && (
<Dropdown.Item onClick={() => setMenu("main")}>
Main menu
</Dropdown.Item>
)}
</Dropdown.Menu>
</Dropdown>
);
};
Working example available here: https://codepen.io/ablewhite/pen/BawdVpg
If I understand your question correctly you want a dropdown menu with another dropdown menu inside of it? The following code would acomplish that, I would suggest using autoClose={false) to prevent the menu from closing.
import { Dropdown } from "react-bootstrap"
import { useState } from "react"
const Mydropdown = (props) => {
const [menu, setMenu] = useState("main")
return (
<Dropdown autoClose={false}>
<Dropdown.Toggle>
Main Menu
</Dropdown.Toggle>
<Dropdown.Menu>
<Dropdown.Item onClick={() => setMenu("secondary")}>Action 1</Dropdown.Item>
{menu === "secondary" &&
<Dropdown autoClose={false}>
<Dropdown.Toggle>
Secondary
</Dropdown.Toggle>
<Dropdown.Menu>
<Dropdown.Item onClick={() => setMenu("main")}>Action 2</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
}
</Dropdown.Menu>
</Dropdown>
)
}
export default Mydropdown

How to stop propagation outside item rows

Research on propagation has not given me answers. I have a React list using divs. Each item has a clickable row that takes the user to another page. But within the row, there are dropdown elements for updating the item. stopPropagation has been added to the dropdowns so that the user does not go to another page.
However, if you open the dropdown and then click off to the side because you changed your mind, the row action gets triggered anyway and takes the user to another page. How do I stop that from happening? I cannot target only the row container for redirecting to another page because I can't seem to add a reference to that element that I can match on in event.target. Is there another way to achieve this?
DROPDOWN
const select = () => (
<Select value={value} onChange={(e) => onChange(e)}>
{items.map((option) => (
<MenuItem className={menuItemClass} key={option.id} value={option.id}>
{option.name}
</MenuItem>
))}
</Select>
)
ONCHANGE
const onChange = async (event) => {
event.stopPropagation();
updateItem();
...
ROW
<TableRowBody onClick={(event) => onClickRow(rowItem, event)}>
...
<select />
...
</TableRowBody>
ONCLICKROW
const onClickRow = (value, event) => {
setValue(value);
};
If the clickable element can be a sibling of the select, you can do a css hack like this:
select:focus ~ .clickable-div{
pointer-events:none
}
.clickable-div{
position: absolute;
height: 100%;
width: 100%;
}
When the select is open (focus), it's sibling, .clickable-div will not respond to clicks.
<TableRowBody >
....
<div onClick={(event) => onClickRow(rowItem, event)} className="clickable-div"></div>
<select />
...
</TableRowBody>

How can I reduce the number of buttons?

I need to have only one button doing two things one by one. I have two buttons "Обучить" and "Генерировать" and they both do something onClick. Is it possible to do it all with one button?
<Button
variant="contained"
color="primary"
style={{
background:
"linear-gradient(45deg, #00ACD3 30%, #00BE68 90%)"
}}
onClick={this.parseInput}
>
Обучить
</Button>
<Button
variant="contained"
color="primary"
style={{
background:
"linear-gradient(45deg, #00ACD3 30%, #00BE68 90%)"
}}
onClick={() => {
this.props.updateData(this.state.filterArray);
}}
>
Генерировать
</Button>
Of course you can. You can use a variable to decide what action you want to perform on onClick function
onClick={() => {
if(this.props.type == 'update')
this.props.updateData(this.state.filterArray);
else
this.parseInput()
}}
You can use same property type (or anything you like) to render different labels
{{this.props.type=='update'? 'Генерировать': 'Обучить'}}
I assume that you want to make a component reusable? In that case you might want to design your component like so.
const NewButton = ({onClick, children}) => (
<Button
variant="contained"
color="primary"
style={{
background: "linear-gradient(45deg, #00ACD3 30%, #00BE68)
}}
onClick={onClick}>
children
</Button>);
Then your above snip becomes
<NewButton onClick={someFunction}>Something</NewButton>
<NewButton onClick={otherFunction}>Something</NewButton>
This is the basic idea behind any component-based front-end framework and I suggest you go to the react tutorials to learn more.
Otherwise, there are plenty of good articles about the topic out there
If I understand you correctly, you want to use the same component but make them do different things, on click for example.
Here is an example of how you can do it.
import React from 'react';
const Button = ({onClick}) => {
return (
<button
onClick={onClick}
>
Click on me
</button>
)
}
export default Button;
So when you want to pass down a method to the button, you can do it like this.
import Button from './button';
clickerTicker() {
alert("First method, clickerticker");
}
secondaryClick() {
alert("Second method yo");
}
render() {
return (
<div>
<ButtonComponent onClick={this.clickerTicker.bind(this)}/>
<br/>
<ButtonComponent onClick={this.secondaryClick.bind(this)}/>
</div>
);
}
Just pass the onClick to the component

Resources