I am using React Materialize and React Router Dom. I'm trying to show navlinks to only authenticated users, so I'm using conditional rendering. But if I do it like below, navlinks are rendered vertical, not horizontal as usual. Is there a solution for that? Thanks
<Navbar>
{isAuthenticated && (
<>
<NavLink to="/locations" href="/locations">
Locations
</NavLink>
<NavLink to="/categories" href="/categories">
Categories
</NavLink>
</>
)}
</Navbar>
Looks like the Navbar component is rendering all of its children inside li elements. When you wrap them in a fragment, the component considers this as its only child element and puts all of the NavLink elements in a single li.
I can think of two simple approaches to handle this:
If there are only a few links, you can do conditional rendering for them:
<Navbar>
{isAuthenticated && (<NavLink to="/locations" href="/locations">Locations</NavLink>)}
{isAuthenticated && (<NavLink to="/categories" href="/categories">Categories</NavLink>)}
</Navbar>
However, this solution is not reliable, especially if you have a lot of links.
Store the NavLink elements in some array and conditionally add the auth-dependant items:
// In the component:
const links = [/* some public links */];
const privateLinks = [
// Don't forget keys. The numbers here are only for example.
<NavLink key={1} to="/locations" href="/locations">Locations</NavLink>,
<NavLink key={2} to="/categories" href="/categories">Categories</NavLink>
];
if(isAuthenticated){
links.push(...privateLinks);
}
// In the template:
<Navbar>{links}</Navbar>
The logic with the links arrays is pretty trivial (with setting private links as the last items) only to keep the demo more simple.
Related
I have a react component looks like this:
<MenuSection backgroundurl="/images/home-slide-4-1920x800.jpg" fluid>
<MenuContainer>
<MenuRow>
{categoryItems.map(categoryItem => (
<MenuItemCard key={categoryItem.Id}>
<Link to={categoryItem.CategoryUrl}>
<MenuItemCardImg src={categoryItem.ImageUrl} />
<MenuItemCardBody>
<MenuItemTitle>{categoryItem.CategoryName}</MenuItemTitle>
</MenuItemCardBody>
</Link>
</MenuItemCard >
))}
</MenuRow>
</MenuContainer>
</MenuSection>
When I click the Link, the route is correct and it takes me to the correct page. However all the CSS in that following page is not applied. If I reload the page that 'Link' took me to the CSS is applied correctly.
When I change the 'Link' and replace it with an < a > tag, when I click the link everything works the next page has the css applied correctly with out any problems.
<MenuSection backgroundurl="/images/home-slide-4-1920x800.jpg" fluid>
<MenuContainer>
<MenuRow>
{categoryItems.map(categoryItem => (
<MenuItemCard key={categoryItem.Id}>
<a href={categoryItem.CategoryUrl}>
<MenuItemCardImg src={categoryItem.ImageUrl} />
<MenuItemCardBody>
<MenuItemTitle>{categoryItem.CategoryName}</MenuItemTitle>
</MenuItemCardBody>
</a>
</MenuItemCard >
))}
</MenuRow>
</MenuContainer>
</MenuSection>
I can't figure out why this is happening, this is the first React project I have done, so maybe I am missing something really obvious?
UPDATE:
When I use import {Nav} from "react-bootstrap"; (Nav.Link) - from react bootstrap, when I click the link the page navigates correctly and the CSS is applied correctly.
<MenuSection backgroundurl="/images/home-slide-4-1920x800.jpg" fluid>
<MenuContainer>
<MenuRow>
{categoryItems.map(categoryItem => (
<MenuItemCard key={categoryItem.Id}>
<Nav.Link href={categoryItem.CategoryUrl}>
<MenuItemCardImg src={categoryItem.ImageUrl} />
<MenuItemCardBody>
<MenuItemTitle>{categoryItem.CategoryName}</MenuItemTitle>
</MenuItemCardBody>
</Nav.Link>
</MenuItemCard >
))}
</MenuRow>
</MenuContainer>
So it just seems when I use Link from react-router-dom I get this strange behaviour.
For now I have found a solution by using Nav.Link, but I would really like to know why when I use Link the navigation works but the CSS is not applied untill I reload the page.
Background
We are using #axe-core/react to work on ARIA accessibility in react application. By default, on page reload no issues are reported, but on Select click we are facing various issues with accessibility, according to axe-core/react.
Problems
serious: Page must have means to bypass repeated blocks
https://dequeuniversity.com/rules/axe/4.0/bypass?application=axeAPI
moderate: Document must have one main landmark
https://dequeuniversity.com/rules/axe/4.0/landmark-one-main?application=axeAPI
moderate: All page content must be contained by landmarks
https://dequeuniversity.com/rules/axe/4.0/region?application=axeAPI
In addition to problems, there are 2 more:
moderate: Page must contain a level-one heading https://dequeuniversity.com/rules/axe/4.0/page-has-heading-one?application=axeAPI
serious: Elements must have sufficient color contrast https://dequeuniversity.com/rules/axe/4.0/color-contrast?application=axeAPI
Code
import React from "react";
import ReactDOM from "react-dom";
import axe from "#axe-core/react";
import { BrowserRouter, Route, Link } from "react-router-dom";
import { MenuItem, Select, InputLabel } from "#material-ui/core";
const BypassRepeatedBlocks = () => (
<div aria-hidden="true">
<a href="#select-content" style={{ display: "none" }}>
bypass repeated blocks
</a>
</div>
);
const NavigationSample = () => (
// we must have <nav> here in order to put content inside landmark. Otherwise we would get issue.
<nav>
<ul>
<li>
<Link to="/home">Home</Link>
</li>
</ul>
</nav>
);
const HomePage = () => <div></div>;
const App = () => (
// BrowserRouter, Route and <Link> (NavigationSample) are the cause of bypass repeated blocks issue
<BrowserRouter>
{/**but with BypassRepeatedBlocks portion of code, it can be solved */}
<BypassRepeatedBlocks />
<NavigationSample />
<main>
<Route path="/home" component={HomePage} />
{/**without <h1> there is heading issue on page loading */}
<h1>H1</h1>
<InputLabel id="my-input" style={{ color: "black" }}>
Sort
</InputLabel>
<Select
id="select-content"
labelId="my-input"
value="item1"
style={{ width: "200px" }}
>
<MenuItem key="item1" value="item1">
item1
</MenuItem>
<MenuItem key="item2" value="item2">
item2
</MenuItem>
</Select>
</main>
</BrowserRouter>
);
axe(React, ReactDOM, 1000);
ReactDOM.render(<App />, document.getElementById("root"));
Observations
As with shown code, when drop-down menu is shown I can navigate through menuitems (options) with key down and key up from keyboard, and that is what is important, from accessibility point of view, right?
After clicking on Select and inspecting HTML elements of web page, I can see two things: first, that my main content gets hidden <div id="root" aria-hidden="true">, and second that new <div> is generated with role='presentation' which contains this drop-down content. And it is outside of <main> which sort of explains the issue All page content must be contained by landmarks, as well as Document must have one main landmark and Page must contain a level-one heading
In addition to previous observation, I tried to add MenuProps={{ disablePortal: true }} to Select element, and issue All page content must be contained by landmarks was removed but i don't know if and what negative impacts this can have
from what i read on internet on this topic, I find this case (select, drop-down) to be very specific and advance.
Questions
Is there a proper way to handle mentioned issues for Select element? How?
Is this one of the situations to be considered as a trade-off (don't have to / can't get rid of all issues)?
If 2. is true, is then that bug to axe-core/react tool?
Let me know if I can provide more details, or update some content.
Thanks.
EDIT
Github repo for reproduction: https://github.com/StefanZivkovic/aria-accessibility-problems.
When you git clone it from cmd, navigate to cd aria-accessibility-problems, execute npm i, thennpm start and wait for server to start. If you use VS code, from cmd do code . to open the files and check the code if you want.
By the way, i found how to overcome Page must have means to bypass repeated blocks, at least on this simple example. That particular issue was caused by react-router-dom, and its elements, Link, BrowserRouter and Route. Might be useful for some.
I'm trying to build a React component that shows multiple images stored in a database, where below each thumbnail image there is a button linking to its dedicated page. Now, I would like each button to show the dominant color of the image as background on hover. I already have the colors stored in database (artwork.palette.DOMINANT), but struggles to pass the hex code to the React component.
The problem is, inline style cannot set properties of pseudo selectors like a:hover, and because the color is dynamically fetched with the artwork object, I can not set it in a global and static css. Is there a way to set component-scoped style in React? Presumably like <style scoped>...</style>.
Here is my code, I only kept the gist of it for simplicity's sake.
const ImageBox = ({ artwork }) => {
return (
<Fragment>
<div className="image">
<img src={artwork.resources.THUMBNAIL} alt={artwork.title} />
</div>
<div className="caption">
<span>By {artwork.artist}.</span>
</div>
<div className="button">
<a href="!#" style={{ color: artwork.palette.DOMINANT }}>
Details
</a>
</div>
</Fragment>
);
};
You could use JS to modify the global properties of CSS.
Declare properties in index.css or App.css and add your basic styles that will utilize these variables.
:root {
--color-surface: white;
}
button {
background: var(--color-surface);
}
Modify these properties using JS(onMouseEnter and onMouseLeave). i.e.
//onMouseEnter
document.documentElement.style.setProperty("--color-surface", "black");
//onMouseLeave
document.documentElement.style.setProperty("--color-surface", "white")
There a few references you can follow:
Blog and CodSandBox
Note: Not sure if it's a good practice(I haven't seen in projects I've worked on), I would recommend using CSS-in-JS or libraries such as styled component.
Thanks to #PramodMali, I found the CSS-in-JS approach as a elegant way to solve this problem. For anyone who stumbles upon the same struggle in the future, here's how I solved it with react-css:
import { createUseStyles } from "react-jss";
const useStyles = (dominantColor = "#fff") =>
createUseStyles({
toDetailPage: {
color: dominantColor,
"&:hover": {
color: "#000",
background: dominantColor,
}
}
});
After defining the style generator, use it to dynamically set classes in component:
const ImageBox = ({ artwork }) => {
const classes = useStyles(artwork.palette.dominant)();
return (
<Fragment>
<div className="image">
<img src={artwork.resources.THUMBNAIL} alt={artwork.title} />
</div>
<div className="caption">
<span>By {artwork.artist}.</span>
</div>
<div className="button">
<a href="!#" className={classes.toDetailPage}>
Details
</a>
</div>
</Fragment>
);
};
This will then generate dynamic classes on render, for instance detailsButton-0-2-7, which applies the properties passed to the generator function when defining classes.
I was looking for how to set styles dynamically. My main intention was to write a style for hover. Using state to track whether it is hovered or not is good but that will cost render for each update. So I thought How I can solve it with out any state change. I end up doing this and it works perfectly.
<a
target='_blank'
href="#"
onMouseOver={(e) => e.target.style.color = 'red'}
onMouseOut={(e) => e.target.style.color = ''}
>
This is my link
</a>
I'm doing a Dropdown menu using Ant Design:
import React, { Component } from "react";
import { Icon, Dropdown, Menu } from "antd";
const { Item } = Menu;
class NotificationBell extends Component {
render() {
const menu = (
<Menu>
<Item><p>You must be connected to get notifications.</p></Item>
</Menu>
);
return (
<Dropdown overlay={menu} trigger={['click']}>
<Icon type="bell" theme="outlined" />
</Dropdown>
);
}
}
Ant this is what I get:
But I don't want to just remove the highlight, I also want to make the component unclickable, i.e. instead of a "hand cursor" I want a "normal cursor".
Adding the selectable={false} prop on the Menu component as suggested by the Ant Design documentation doesn't help. What should I do?
Thank you for your help.
The documentation you linked to specifies a disabled prop on Menu.Item which may or may do what you want. If you want to do something other than what the library provides, you can customize the behavior.
You can use the CSS property cursor to specify which cursor you want on hover.
You might want to use not-allowed for a disabled-style cursor, or the default arrow: default.
Docs
For future reference, you can't prevent a user from clicking on the element. What you want to do is actually to communicate the affordance (or lack thereof) using visual cues, and potentially alter the behavior of your application when receiving that input.
The CSS property pointer-events set to none makes the component ignore mouse events without altering the style of the cursor.
<Menu>
<Menu.Item key="/">
<Link href="/">Clickable</Link>
</Menu.Item>
<Menu.Item style={{ pointerEvents: 'none' }}>
Unclickable
</Menu.Item>
</Menu>here
I need to implement a Bootstrap-style collapsible panel in React that displays arrow glyphs in the panel header similar to this No JS required example.
I've implemented most of this functionality using react-bootstrap in an unintended way, that results in this browser warning:
< a> cannot appear as a descendant of < a>. See ProjectInfo > a > ... > Panel > a.
Here is the render function of my component:
render() {
const projectHeader =
<a className="accordion-toggle collapsed">
<h4 className="panel-title">Project Information</h4>
</a>;
return (
<Accordion>
<Panel header={projectHeader} eventKey="1">
Project info goes here.
</Panel>
</Accordion>
);
}
Can the requirement for toggling panel header icons be be met using react-bootstrap? If so, how? If not, how is it done using React?
UPDATED 07-DEC-2017: I solved my problem by adding an anchorClass property to the react-bootstrap Panel component in a forked repo.
This is final render function of my component:
render() {
const header = <h4>Project Information</h4>;
return (
<PanelGroup accordion>
<Panel header={header} anchorClass="panel-toggle" eventKey="1">
Project info goes here.
</Panel>
</PanelGroup>
);
}
The panel-toggle classes were defined like those in the No JS required example.