Passing JSX into dangerouslySetInnerHtml - React - reactjs

Background
I am working on a React application. I need to place some HTTML inside a div. But I need to pass a tooltip package inside the HTML I pass. I want to eventually only have the tooltip trigger on certain words. For now, I just want the package to show up when I wrap the entire html element string
Code
I basically want to do this
import Tippy from '#tippy.js/react';
const App = () => {
const myHtmlString = '<p>Hi</p>`;
const text = <Tippy content={myHtmlString} />;
return (
<div dangerouslySetInnerHtml={{ __html: text }} />
)
}
Problem
This renders [object Object]
I would like to render the text surrounded by the tooltip instead.

Related

React Jest Testing : How to test image is left aligned according to input parameter

I have to create react component having image and text box, so My task is to test
Image or Text box is Left or right aligned , as needed
Image or Text box is Top or bottom aligned, as needed
By passing input variant as textLeft or textRight
Example Component
Any Help?
If they both had the same aria-label or another selector you could use the getAllBy query which would give you an array of items in order they find them so then you could check the children by using within to query inside those.
Something like this would work.
const Component = () => {
return (
<>
<div aria-label="card">
<img alt="image example" />
</div>
<div aria-label="card">
<h1>This is heading</h1>
</div>
</>
)
}
import { render, screen, within } from '#testing-library/react'
test('React Testing Library works!', () => {
render(<Component />)
const cards = screen.getAllByLabelText('card')
expect(cards).toHaveLength(2)
expect(within(cards[0]).getByAltText('image example'))
expect(within(cards[1]).queryByAltText('image example')).not.toBeInTheDocument()
expect(within(cards[1]).getByText('This is heading'))
expect(within(cards[0]).queryByText('This is heading')).not.toBeInTheDocument()
})

How to test using jest office-ui-fabric-react CallOut component?

I've been trying to test Callout component in my react project.
For simplification, following is React render component:
<div className="UserInfoDiv">
<div ref={this.menuButtonElement}>
<ActionButton id="toggleCallout"
onClick={changeIsCallOutVisibleProptoTrue}
text="Show Callout" />
</div>
<Callout
className="calloutClass1"
target={this.menuButtonElement.current}
hidden={!this.props.isCalloutVisible}>
<div id="callOutContainer">
<span>Need to test items here.<span>
<button className="clickForSomeAction">Simulate Click on this</button>
</div>
</Callout>
</div>
This works absolutely fine in UI. For testing in jest, I tried following:
userMenu = mount(<UserInfoDivComponent {...props} />);
UserInfoDiv.find("button#toggleCallout").simulate('click');
expect(changeIsCallOutVisibleProptoTrue.mock.calls.length).toBe(1);
userMenu.setProps({isCalloutVisible: true });
// Following only gives html(included UserInfoDiv,toggleCallout) `without html from callout`:
console.log(userMenu.html());
I need help on, How to test following scenarios?
Callout is Visible?
Find .clickForSomeAction button inside Callout.calloutClass1 and simulate click
There are similar component (ex: DropDown, Contextual Menu) from office-fabric-ui which renders HTML in document and not in current component HTML.
Finally, I did testing of Callout using ReactTestUtils as given in examples:
let component:any;
const container = document.createElement('div');
document.body.appendChild(container);
let threwException = false;
try {
component = ReactTestUtils.renderIntoDocument(<UserInfoDivComponent {...itemProps} />);
} catch (e) {
threwException = true;
}
expect(threwException).toEqual(false);
const UserInfoDiv= ReactTestUtils.findRenderedDOMComponentWithClass(component, "UserInfoDiv");
const clickForSomeAction = ReactTestUtils.findRenderedDOMComponentWithClass(component, "clickForSomeAction");
ReactTestUtils.Simulate.click(clickForSomeAction);
it works as expected, which few glitches as we can not query ReactTestUtils directly by querySelectors.
Edit 1
we can query using XML selectors.

`rexxars/react-markdown` plugin usage or alternative for rendering React markdown

I'm trying to render some html, stored in a DB, and put a component inside.
It'd look like this:
import ReactMarkdown from 'react-markdown/with-html';
const inlineCode = (props) => <Gist id={props.value} />;
const source = '`7df0c9a5d794504a28bd3256b7bf5c4f` <p>asdasdasd</p><h1>title</h1>';
ReactMarkdown is used like this:
<ReactMarkdown source={source} renderers={{ inlineCode }} escapeHtml={false} />
The result is is rendered properly and the block is also, but isn't, the contents are outside of the block.
If I wrap the whole source with a <div>, the <Gist/> is rendered as text and <p>/<h1> are rendered properly.
What am I missing? I'm trying to store html with custom components inside, <Gist/> is just an example. Suggestions for a (more) suitable library are also welcome. Example ideal source I'd like to store in a db and then render in a React component:
<div>
<p>
<CustomReactComponent/>
<br/>
test
</p>
<Gist/>
</div>
Okay I found this lib: https://github.com/probablyup/markdown-to-jsx
If your source looks like:
const source = `<gist id="yourIdHere" /> <h1>asdasdasd</h1>`;
<Markdown
options={{
overrides: {
gist: {
component: renderGist,
},
},
}}
>
{content}
</Markdown>
It renders both the <Gist> and the normal <h1> as <h1. Paragraph tags seem to be automatically added if you add line breaks and something like # Title is also automatically wrapped.
<Gist> in source is converted to lowercase, so it'd only matter for the overrides object (lowercase again). Handles all my custom stuff and html predictably.

Dynamically changing React component tag

I'm creating a Landing page for a project and want to reduce my code by making a helper function to display my four different paper components. Everything seems to be working correctly except for displaying my Icon components that are within the papers.
When I console log Icon it is the correct text, yet the component doesn't appear on the page and I receive these two warnings for each component tag:
"Warning: The tag <CardTravelIcon> is unrecognized in this browser. If you
meant to render a React component, start its name with an uppercase letter."
and
"Warning: <CardTravelIcon /> is using uppercase HTML. Always use lowercase
HTML tags in React."
If I just hard code in CardTravelIcon or any of the other 3 component names in that exact format instead of using Icon from my map function, everything works as expected. Below is the code for my helper function:
class Landing extends Component {
renderPapers() {
const classes = this.props.classes;
return _.map(infoPapers, ({ description, Icon }) => {
return (
<Grid item xs={6} sm={3} key={Icon}>
<Paper className={classes.paper}>
<Icon className={classes.paperIcons} />
{description}
</Paper>
</Grid>
);
});
}
I'm at a loss and would appreciate any help. Thank you.
So you want to pass a component as a variable, right?
Let's say you have this minimal CardTravelIcon component:
const CardTravelIcon = ( props ) => (
<div className={ props.className }>Card Travel Icon</div>
);
And infoPapers data like this (Icon is a reference to the component):
const infoPapers = [
{
description: "Paper 1 description",
Icon : CardTravelIcon
}];
You didn't show us the infoPapers data, but I suspect you're trying to pass a string as the component, e.g. { Icon : "<CardTravelIcon />" } and expect it to work like setting innerHTML (rendering HTML from a string). This is not the case in React, the JSX code needs to be transpiled into calls to React.CreateElement first, and it isn't done by parsing strings.
If you pass references to components everything should be rendered just fine with the following render method (note: removed lodash in favor of native map method, for clarity):
class Landing extends React.Component {
render() {
return infoPapers.map(({ description, Icon }, idx) => {
return (
<div key={ idx }>
<Icon />
{description}
</div>
);
});
}
};
Here's a working example: https://jsfiddle.net/svygw338/

How can I render a <template /> tag with react?

Sometimes you might need to render web-components from your react app.
Web-components often use a special <template> ... </template> tag.
But if I try to render such markup with react like this:
render() {
return (
<template>
<div>some content</div>
</template>
)
}
then my web-components don't work correctly.
The reason is that JSX does a different job than what the <template /> tags exists for. The idea of a template tag is to not render its children and pretty much handle it like unparsed text (the browser actually parses it just to make sure its valid html, but does nothing more)
But when you write this in JSX:
return (
<template>
<div>some content</div>
</template>
)
you're basically instructing react to create a 'template' element and then create a 'div' element and then to append this div to the template as a child.
So under hood this happens:
const template = document.createElement('template')
const div = document.createElement('div')
const text = document.createTextNode('some text')
div.appendChild(text)
template.appendChild(div)
But what you want is to set the contents of the <template /> as a string. You can use innerHTML for that.
Solution
One solution would be:
render() {
return (
<template
dangerouslySetInnerHTML={{
__html: '<div>some content</div>'
}}
/>
)
}
Now you're asking react to create all those children tags as node elements but letting the browser decide what to do with them.
Nicer solution
You might not want to use dangerouslySetInnerHTML all the time. So let's create a helper component:
function Template({ children, ...attrs }) {
return (
<template
{...attrs}
dangerouslySetInnerHTML={{ __html: children }}
/>
);
}
Now any time you need to use a template you can use it like this:
render() {
return (
<Template>
{'<div>some content</div>'}
</Template>
)
}
Don't forget to put the inner content in quotes, because it should be a string.
I know that this question has already an answer, but there is another, I guess simpler solution to do that creating hoc (Higher Order Component).
Just create new "component" like this:
// hoc/Template.js
const template = props => props.children
export default template
and then you can use it in your project this way:
import './hoc/Template.js'
...
render() {
return (
<Template>
{'<div>some content</div>'}
</Template>
)
}
Newer version of react has already build it such a component, so you can achieve same thing without creating component.
import { Fragment } from 'react'
...
render() {
return (
<Fragment>
{'<div>some content</div>'}
</Fragment>
)
}
This is actually a bug in React! https://github.com/facebook/react/issues/19932
When the native browser parses the <template> tag, it appends all children to the template's content fragment, instead of adding them as children. So a template should never actually have any children.
However, if you programmatically build the DOM (the way react-dom does), then the template will have an empty content fragment, since all children are added as children. This is why web components will not behave properly with these templates.
Here's an example of the problem too: https://codesandbox.io/s/new-sun-8l62o?file=/src/App.tsx:1056-1118
The easiest workaround is to use dangerouslySetInnerHTML, like so:
<template dangerouslySetInnerHTML={{ __html: `
<p> Some text </p>
` }} />
This is limited by the fact that you can only supply raw HTML (no custom React elements). However, since you're using the <template> tag in the first place, it seems highly unlikely that you'd also be using React within the template.

Resources