Why does tabIndex not work after first tab to select li? - javascript

You can view my app here: https://moon.holdings
Here is the repo: https://github.com/Futuratum/moon.holdings
If you select the [ + ] Add Asset button, click in the search input and hit tab there are 2 issues.
Nothing is selected the first time, you have to tab again in order to select the first asset.
And more importantly after Bitcoin is selected, tabbing does not select the next item in the list. Instead after 4 tabs the I can see that the Coinbase button was selected instead of another li.
Here you can see that each li does correctly have a tabindex:
1st tab, nothing selected
2nd tab, Bitcoin selected
3rd tab, nothing selected
4th tab, Coinbase button selected:
The JSX of the searchModal.js component:
render() {
const { assets } = this.state;
return (
<section id="search-modal">
<header className="search-header">
<input
id="coin-search"
type="text"
placeholder="Search"
onChange={() => this.handleChange()}
/>
<button className="close-modal-x" onClick={this.closeSquareEdit} />
</header>
<ul id="coins-list">
{ assets !== 'undefined'
? assets.map((asset, i) => (
<li
key={asset.currency}
role="button"
tabIndex={i}
onFocus={() => this.setFocus(asset)}
onBlur={this.onBlur}
onClick={() => this.handleSelect(asset)}
>
{asset.name}
<span className="symbol">{asset.currency}</span>
</li>))
: <li>Loading...</li>
}
</ul>
</section>
);
}
The main container: Board.js
return (
<div id="board">
{ this.renderPortfolio(sortedAssets) }
{ edit && this.renderSquareEdit(coin) }
{ search && this.renderSearchModal() }
{ loading && moonPortfolio && <Loading /> }
{ portfolio.length === 0 && <Welcome /> }
<PlusButton toggleSearch={this.handleSearchButton} />
<Affiliates />
<Nomics />
<Astronaut logo={isTrue} />
</div>
);
The renderSearch method:
renderSearchModal() {
return (
<div>
<Search
handleClose={() => this.toggleSquareEdit(false, {})}
openEdit={this.toggleSquareEdit}
/>
<div id="overlay" onClick={this.handleSearchButton} />
</div>
);
}
Finally the affiliates.js component
const links = [
{ name: 'coinbase', link: coinbase, h4: 'Buy Bitcoin' },
{ name: 'binance', link: binance, h4: 'Buy Altcoins' },
{ name: 'changelly', link: changelly, h4: 'Swap coins' }
];
export default () => (
<div className="affiliates">
<ul>
{links.map(l => (
<a href={l.link} key={l.name} target="_blank" rel="noopener">
<li className={l.name}>
<h4>{l.h4}</h4>
</li>
</a>
))}
</ul>
</div>
);

Well, tabindex doesn't work the way you think it works.
When you select the input and press tab, it goes to the button next. Which is the next focusable element. Then ul then first li, then to the open/close button then to coinbase button
Using positive tabindex is not encouraged either.
https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex
https://developer.mozilla.org/en-US/docs/Web/Accessibility/Keyboard-navigable_JavaScript_widgets
You should be fine without the tabindex property for li elements. As you could always use arrow keys to navigate the select box items.
Also check this one here: https://medium.com/#andreasmcd/creating-an-accessible-tab-component-with-react-24ed30fde86a
Which describes how to use role property, which can also be deployed to help control focus flow.

Related

How to change .jsx component style from inside the .js file? React

function Navbar() {
const [shownavcontents, setShownavcontents] = useState(false)
if(shownavcontents){
document.getElementsByClassName("navbardivofmobiledevice").style.display = "none";
}else{
document.getElementsByClassName("navbardivofmobiledevice").style.display = "block";
}
return (
<>
<div className="top">
<Searchbar />
<AiOutlineMenu size={20} className="outlinemenu" onClick={() => {setShownavcontents(true)}} />
</div>
<div className="navbardivofmobiledevice">
<ul>
<li>
Home
</li>
<li>
Members
</li>
<li>
All Posts
</li>
<li>
My Posts
</li>
</ul>
</div>
</>
);
}
As you see I am trying to make responsive navbar, in this case, for mobile devices. I've faced one problem. I've made button on top of navbar and some navbar contents which I want to display only whenever user will click this button and vice versa. So I tried using hooks to check if the user clicked the button which works perfectly, only thing that doesn't works is this if else statements it seems like document.getElementsByClassName("navbardivofmobiledevice").style.display = "none"; doesn't have an effect here. So my question is what is the alternative of this? What can I do here?
This is imperative code:
document.getElementsByClassName("navbardivofmobiledevice").style.display = "none";
With React, you rarely get references to DOM elements and update them manually, and in any case, you do it using Refs, not with the getElement... or querySelector... methods). Instead, you write declarative code and let React take care of the DOM updates for you.
In this case, simply add or remove a hidden attribute or CSS class that has display: none from your JSX:
function Navbar() {
const [shownavcontents, setShownavcontents] = useState(false);
return (
<>
<div className="top">
<Searchbar />
<AiOutlineMenu size={20} className="outlinemenu" onClick={() => {setShownavcontents(true)}} />
</div>
<div className="navbardivofmobiledevice" hidden={ !shownavcontents }>
<ul>
<li>
Home
</li>
<li>
Members
</li>
<li>
All Posts
</li>
<li>
My Posts
</li>
</ul>
</div>
</>
);
}
If you prefer to use a class, assuming you have defined a CSS class .isHidden { display: none; } you would use this line instead:
<div className={ `navbardivofmobiledevice${ shownavcontents ? '' : ' isHidden' }` }>
Regarding what some comments are mentioning about rendering that conditionally like so:
function Navbar() {
const [shownavcontents, setShownavcontents] = useState(false);
return (
<>
<div className="top">
<Searchbar />
<AiOutlineMenu size={20} className="outlinemenu" onClick={() => {setShownavcontents(true)}} />
</div>
{ shownavcontents && (
<div className="navbardivofmobiledevice">
<ul>
<li>
Home
</li>
<li>
Members
</li>
<li>
All Posts
</li>
<li>
My Posts
</li>
</ul>
</div>
) }
</>
);
}
I would avoid that, as hiding your main navigation from Google and other search engines will harm your SEO. You need to hide it visually but still have it in the DOM.
If you want to do better than that, add all the appropriate ARIA attributes and logic for a navigation menu with nested submenus, as explained here:
https://www.w3.org/WAI/ARIA/apg/example-index/menubar/menubar-navigation

Change input value after click on list element

I have got form with input
<form onSubmit={handleSubmit}>
<input type="search"
onChange={handleChange}
onFocus={() => setFocus({name: true})}
onBlur={() => setFocus({name: false})}
value={inputName}
/>
</form>
and the list of elements which is rendered after this input field has focus. This input field is live searcher for elements of this rendered list.
the code to render list:
<div>
{
objects.map((object, index) => {
return (
<ul>
<li key={index}>
{object.name}
</li>
</ul>
)
})
}
</div>
I want to make the action that when I click on the list element, the input field gets this element value.
In the picture below, the input field should be Object 2 after I click on Object 2 element
Due to there are two independent jsx objects, I have no idea how can I do this.
You can try to do this with a function triggered onClick on a <li> that sets the inputName value with the object.name :
<div>
<ul>
{
objects.map((object, index) => {
return (
<li key={index} onClick={() => setInputName(object.name)}>
{object.name}
</li>
)
})
}
</ul>
</div>
And by the way I think your <ul> tag should be outside the map

How to open active menu when user visit manually url

I think it might be silly question to ask but trust me I am new to React . Actually I am working on app where I have different collapsible menu. For example when I click on menu then dropdown show what I want to achieve when user copy manually link and put in the browser then I want to show specific menu . could someone please help me how to achieve this goal .
code
menus.map((item, index) => (
<UnlockAccess
currentUser={userType || null}
roles={item.roles}
key={index}
>
<div key={index}>
<p
onClick={() => {
this.RouteTo(`/${item.url}`);
this.toggle(item.id);
}}
className={`element ${this.state[item.activeTab]}`}
>
<span
className={
collapse === item.id ? "fa fa-minus" : "fa fa-plus"
}
key={index}
></span>
<p className={this.state[item.activeTab]}>
{item.name}
</p>
</p>
{collapse === item.id ? (
<div className="prop-child">
<ul>
{item.children.map((item, index) => (
<li
key={index}
className={this.state[item.activeTab]}
onClick={() =>
this.setState({ collapse: item.itemId }, () =>
this.toggle(item.itemId)
)
}
>
<Link
onClick={() => this.RouteTo(`/${item.url}`)}
to={item.url}
className={this.state[item.activeTab]}
>
{item.name}
</Link>
</li>
))}
</ul>
</div>
) : null}
toggle = (id) => {
if (!this.state.collapse || id !== this.state.collapse) {
this.setState({
collapse: id,
});
}
};
Just check the url params in the componentDidMount of your class. Then open the menu you want to be opened.

How to index div blocks so that I can access different properties from different divs?

I'm working on a personal project, and one of the features that I'm doing is a pdf-uploader where you can upload pdfs & view them.
THE ISSUE:
The div blocks pop up fine, but it ONLY shows the most recently uploaded PDF. For example, if I uploaded my resume and then uploaded an assignment, when I go to click the view button on the resume's div block, it would display my assignment and NOT the resume. This is really holding me back from progressing.
In the end, I want to be able to view the files associated with the individual div blocks which I believe could be achieved using indexing. I'm pretty new to ReactJS, though, so I'm kinda going through the learning curves. I attached my code so that you can see how it works (super simple to use.)
CURRENTLY:
In the screenshot below, you'll see that I uploaded a PDF called "Job Shadow", and then I uploaded a PDF called "Workshop agenda". When I click on the view button for the Job Shadow div, it displays the workshop agenda pdf:
CODE:
import { FilePicker } from 'react-file-picker';
import Button from 'react-bootstrap/Button';
import Modal from 'react-awesome-modal';
import '../styles/transcripts.css';
class Transcripts extends Component {
constructor() {
super();
this.state = {
title: "",
visible: false,
uploadedFiles: [],
fileDisplay: ""
};
}
// Simply opens the modal
openModal() {
this.setState({
visible: true
});
}
// Closes the modal
closeModal() {
this.setState({
visible: false
});
}
/*
* This method handles the action of 'uploading' a file.
* First it checks if you've already uploaded the file, and if so, it won't allow you to upload it again.
* In the event that it's not uploaded, it gets added into our list of transcripts.
*/
handleFileChange = file => {
if (this.handleCheck(file.name) === true) {
alert("Duplicate file. You've already uploaded this.");
} else {
this.setState({ fileDisplay: URL.createObjectURL(file) }) //creates the URL for the PDF to render
this.setState({ title: file.name });
var joined = this.state.uploadedFiles;
joined.push(file.name);
this.setState({ uploadedFiles: joined });
}
};
// Simply checks whether or not you've uploaded the same file twice.
handleCheck(fileName) {
return this.state.uploadedFiles.some(item => fileName === item);
}
// Methodly simply deletes a file from the list of uploadedFiles if the user desires to (taken from StackOverflow)
delete(item) {
if (window.confirm("Delete '" + item + "'?")) {
const data = this.state.uploadedFiles.filter(i => i !== item);
this.setState({ uploadedFiles: data });
}
}
// This method allows the users to physically the see the contents of the PDF they uploaded.
showFile() {
const { title } = this.state;
return (
<span>
<Button
style={{ backgroundColor: "#f57505", width: "165px" }}
onClick={() => this.openModal()}
>
View
</Button>
<Modal
visible={this.state.visible}
min-width="800"
min-height="200"
effect="fadeInDown"
onClickAway={() => this.closeModal()}
>
<div>
<h1>{title}</h1>
<embed src={this.state.fileDisplay} width="800px" height="700px"/>
<br /><br /><input type="text" />
</div>
</Modal>
</span>
);
}
/*
* This method handles the UI aspect of uploading a file using the FilePicker ReactJS tool.
*/
fileUploader() {
return (
<div>
<FilePicker
extensions={["pdf"]} // Notice that I removed the "."
onChange={this.handleFileChange}
onError={errMsg => alert("Invalid file type.\nMust be a .pdf file")} // Please handle error
>
<Button style={{ backgroundColor: "#f57505", width: "75px" }}>
Upload
</Button>
</FilePicker>
</div>
);
}
/*
* This method physically renders the transcripts in a nicely-formatted manner.
* First checks to see if there are any transcripts, and then it acts accordingly.
*
* No transcripts present = warning message & a button to upload
* Transcripts present = show them & still have the button to upload more
*/
renderTranscripts() {
if (this.state.uploadedFiles.length === 0) {
return (
<div>
<div id="rectanglePadding">
<div id="nothingToShow">
<h1>There are no transcripts to display! </h1>
</div>
</div>
<div style={{ textAlign: "center" }}>
<br />
<br />
{this.fileUploader()}
</div>
</div>
);
}
const noteItems = this.state.uploadedFiles.map(note => (
<span>
<span id="blockStyling">
<span>
{" "}
<a id="textStyling">{note}</a> 
</span>
<p />
<p id="textStyling2">
Potential Issues: <br />
- Short by 2 math credits <br />
- Missing an elective
</p>
<br />
<div style={{ textAlign: "center" }}>
{this.showFile()}
<br />
{/* <Button
class="floated"
style={{ backgroundColor: "#f57505", width: "75px" }}
onClick={this.login}
>
Edit
</Button> */}
<Button
style={{ backgroundColor: "#f57505", width: "165px", }}
onClick={this.delete.bind(this, note)}
>
Delete
</Button>
<br />
<br />
</div>
</span>
<br />
<br />
<br />
</span>
));
return (
<div>
<br />
<h1 style={{ textDecoration: "underline", paddingLeft: "30px" }}>
My Transcripts
</h1>
{noteItems}
<div style={{ textAlign: "center" }}>
<br />
<br />
{this.fileUploader()}
</div>
</div>
);
}
/*
* Simply displays the UI.
*/
render() {
return <div>{this.renderTranscripts()}</div>;
}
}
export default Transcripts; ```

React js - pass value to child to parent to another child

I am trying to pass the data from Child > parent > child
Child
{this.state.data.map((item, index) => (
<li className='card' key={index}>
<span>{item.continent} </span>
<ul className="accordion-body">
{item.regions.map((c, i) => (
<li key={i} onClick={this.props.toggleContent}>
<img src={c.flag}/> {c.country}
</li>
))}
</ul>
</li>
))}
Basically I need to get selected country and some other values from the child and pass to parent
and pass those values to another child.
My Parent
<div className="modal-header">
<h2>Choose your {title}</h2>
<a href="#" className="model-close" data-dismiss="modal" aria-label="Close"><i
className="fa fa-times-circle"></i></a>
</div>
<div className="modal-body">
{showCountry && <CountryList toggleContent={this.toggleContent}/>}
{showLanguages && <RegionList country={country} flag={flag} languages={languages}
toggleContent={this.toggleContentRegion.bind(this)}/>}
</div>
and
toggleContent = () => {
this.setState({
...this.state,
showCountry: !this.state.showCountry,
showLanguages: !this.state.showLanguages,
title: 'language',
country: 'country',
languages: [],
flag: 'flag'
});
}
I tried to use below
<li key={i} onClick={this.props.toggleContent(c.country)}>
<img src={c.flag}/> {c.country}
</li>
and access it from parent
toggleContent = (country) => {
this.setState({
...this.state,
showCountry: !this.state.showCountry,
showLanguages: !this.state.showLanguages,
title: 'language',
country: country,
languages: [],
flag: 'flag'
});
}
But, my components not working correctly When do that and always shows the 2 child component.
Are there any proper way to pass the data to parent from a json array?
So the best way I would handle this would be to make the import your parent class components into the child , place it at the very top of the child JSX but hide it by default. The modal would be fixed, background covering the full page and at a z-index higher than the rest of the child components, so that way only the modal contents are the only accessible things . You would have a state that "toggles on" the modal for each click of the item list and a close button that toggles it off. You would update the modal content and toggle it on for every click
In terms of the second child, you can just show it on the same modal
Found a way to do this :)
render() {
var toggleContent = this.props.toggleContent;
return (
<div className="modal-wrapper">
<ul className="country-list">
{this.state.data.map((item, index) => (
<li className='card' key={index}>
<span>{item.continent} </span>
<ul className="accordion-body">
{item.regions.map((c, i) => (
**<li key={i} onClick={() => toggleContent(c.country,c.flag, c.languages, c.region)} >**
<img src={c.flag}/> {c.country}
</li>
))}
</ul>
</li>
))}
</ul>
</div>
);
}
Changed below line
onClick={() => toggleContent(c.country,c.flag, c.languages, c.region)

Categories

Resources