How to prevent styled-components from stripping custom HTML attributes? - javascript

My project is trying to switch to styled-components, but there is one big issue: our automated QA tests rely on a custom attribute called qa-component appearing in the dom for each HTML element that it is defined for.
The old way we did this worked fine: <div style={ styles.cssModuleStyle } qa-component="big-area" /> would translate to the dom as <div class="whatever" qa-component="big-area" />.
However, when using styled components, the qa-component attribute gets stripped because SC thinks its a prop.
How can I get styled-components to pass this custom attribute to the dom?

What you're looking for is withConfig + shouldForwardProp. It allows you to define what props get passed down. Here's how you can implement the desired behavior:
const StyledTitle = styled.h1.withConfig({
shouldForwardProp: (prop, defaultValidatorFn) =>
defaultValidatorFn(prop) || ['qa-attribute'].includes(prop),
})`
text-decoration: underline;
`;
Take a look at the docs here: https://styled-components.com/docs/api#shouldforwardprop
And here's a playground with this approach: https://stackblitz.com/edit/stackoverflow-71912300

Related

DOM remove method in react js

I want to change remove a certain tag with an id in the html page, eg.<div id="theid"><p>sometext</p></div> Is there any ways to do it with react js? I know I can do it with javascript by document.getElementById("theid").remove();, how can I do it in the react way? I don't need a button or anything, just simply remove it when the page loads. I'd prefer methods without importing any modules or libraries if possible. Thank you.
You should likely use a ref:
https://reactjs.org/docs/refs-and-the-dom.html
So you attach the ref to the DOM element, then you can imperatively remove that element just like you specified.
So in the component function body:
const myRef = useRef();
Then in the component render:
<Component ref={myRef} />
Then you can use the code in your question to remove the element from the DOM.
ref.remove();
If you need to do it when the page loads look at using useEffect to achieve this. Although if you're removing an element on page load, I question why you even need the element there in the first place ;).
If it's rendered as part of React, the right way to do it would be to simply omit it from the source code:
const App = () => (
<div>
<div id="theid">foo</div>
<div>more content</div>
</div>
);
to
const App = () => (
<div>
<div>more content</div>
</div>
);
If it's not part of React, then remove it from whatever process generates the HTML.
If that's not an option - if it must be part of the HTML served to the client and it's not rendered as part of React - then you'll have to resort to doing what you're currently doing:
document.getElementById("theid").remove();
probably completely separate from your React script, since it's something you want to do only once, when the page loads, and not something that needs to be a part of the React lifecycles.

React focus div after it has loaded

I'm currently experimenting with React, and I've now run into an issue that I can't seem to solve.
In my application, I use a React library to handle hotkeys, these hotkeys have a scope, so when I want a certain set of hotkeys to be active in a div, I have to wrap that div with a <HotKeys> tag.
I have to toggle some of these divs, so I'll have something along the lines of
isActive ?
<HotKeys ...>
<div ...>...</div>
</HotKeys>
: <div ...>...</div>
I now need to figure out a way to focus the div when it's created. Pretty much every article on the web suggest something like this:
const focusRef = useRef(null);
useEffect(() => {
focusRef.current.focus()
})
return (
isActive ?
<HotKeys ...>
<div ref={focusRef} tabIndex={-1} ...>...</div>
</HotKeys>
: <div ...>...</div>
)
I've tried some variations, including having the div top level (without the <HotKeys> wrapping them), all to no avail.
When I print the focusRef object in the useEffect method, I do get the expected output, focusRef is correctly set and current is populated, but calling the focus method doesn't work. At one point I tried calling the focus method from a button and manually triggering it after the component had fully loaded, it seemed to work (document.activeElement was being changed), then for some reason it stopped working again. All this leads me to believe that somehow the component hasn't fully loaded, despite calling the useEffect hook, which, if I understand correctly, triggers when the element has rendered for the first time/after every change to state.
I'd really appreciate some help with this, since I basically started learning React yesterday.
You must to use an useCallback , because useRef don't notifies you when ref was created
https://reactjs.org/docs/hooks-reference.html#useref
I think I figured it out.
You were right about using the tabIndex but you needed to pass it in as a string like this:
tabIndex={"-1"}
When you first load it a dotted line box surrounds the div that has the ref attached.
check out this code sandbox:
https://codesandbox.io/s/nifty-wildflower-eqjtk?file=/src/App.js
grabbed from this accepted answer where they pass in a string:
Need to put focus on div in react

Apply motion to react component Framer-Motion

I know that I can apply motion directly to element/HTMLtag like this:
<motion.div>some content</div>
But how can I apply it to this?
<Comp />
Without wrapping it inside another HTML element, like in React-Transition-Group library.
Framer API provides Frame component, but it acts like permanent additional HTML element with own styling, and it is messing my layout.
If anyone comes to this page seeking for the solution of how to apply motion from Framer-Motion library to your React Component and not the direct DOM element like "div" or "span", here it is:
motion.custom()
Example of use:
import { Link } from "react-router-dom"
const MotionLink = motion.custom(Link)
return <MotionLink />
As for today it is not mentioned in the official documentation, or it is in someplace deep and hard to find.
I had found it in BUG reports here, there is a Codesanbox that illustrates my example, created by the person who reported a bug.
motion.custom was deprecated as of v4.0 in favour of motion(Component) or motion("Component").
Your code would simply look like this
const MotionComp = motion(Comp)
return <MotionComp>some content</MotionComp>
Without using any internal fuctions,
You just need to wrap it with any motion element:
<motion.div>
<Comp />
</motion.div>
You can notice such behavior across examples in the docs, like of Side Menu example.

Binding value as html in stenciljs

I am having trouble rendering a value with custom html inside into an element.
ex:
this.title = 'Hello <b> stencil </b>'; << response value from an API
Binding:
<h1>{this.title}</h1>
I am expecting something same as innerHtml behavior in JavaScript.
You can use
<h1 innerHTML={this.title} />
This is not a good practice in JSX, it is against the idea of virtual DOM and it's not creating virtual nodes.
You should try like this
this.salute = 'Hello';
this.name='stencil';
Binding
<h1>{this.salute} <b>{this.name}</b></h1>
Or if it is a more complex situation, build another smaller component.
However using innerHTML will work, but should be used in different situations more details here(at the bottom of the page).

Issue using multiple styles with Material-ui and Radium

I am trying to combine Radium and Material-ui. When I try to apply multiple styles on a single Material-ui component, no style is applied. So, for example, something like this produces no styling applied:
<MenuItem style={[styles.styleOne, styles.styleTwo]} >
Of course, if I do something like:
<MenuItem style={Object.assign({}, styles.styleOne, styles.styleTwo)} >
it works. Is there some way around it or this is the only way to use Radium for combining styles for a Material-ui component? And just to mention, Radium is properly set up, because applying array of styles on, for example, DIV element or works properly.
Also, I am open to any suggestion about styling a React project that uses Material-ui library. Thanks!
For material-ui components in react, we add styles using the className. If i have to add multiple styles in a material component then below are the methods:
Example 1:
<div className={`${style1} ${style2}`}>
Example 2:
import classNames from 'classnames';
<div className={classNames(classes.style1, classes.style2)} />
Specifically for your case (Radium):
What it's doing is merging 2 objects (style1 and style2) into a new anonymous object {} which is what you need to do.
You'll want to be careful when doing this however as you'll need to consider how you merge if both objects define the same key e.g. if style1 and style2 both define a height which do you use?
There's a long list of possible ways to do this on this stackoverflow thread http://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically depending on the libraries you're using and your use case they each have their own pros and cons.
Instead of adding classnames, you can also use the clsx module that comes with Material UI and combine your style classes.
{/* using arrays */}
<MyComponent classes={clsx([classes.text, classes.title])} />
{/* using conditions */}
<div className={clsx(classes.root, {
[classes.base]: true,
[classes.closed]: !open,
[classes.open]: open
})]>
{props.children}
</div>
The Material UI Mini Variant Drawer example does a great job showing this module off.
Check out this fiddle: https://jsfiddle.net/Lxh5x2qr/
It uses the JSX spread (...) operator, which is a bit nicer syntax:
styleOne: {
background: 'blue',
color: 'red'
},
styleTwo: {
background: 'green'
},
... style={{...this.styleOne, ...this.styleTwo}} ...
Please notice the the order of object does matter, just like in Object.assign.
We should not forget that MenuItem is not a DOM element, so when we apply style to it, material-ui manipulates it before applying it to the underlying element, and probably this is the reason why using an array does not work.

Categories

Resources