FormattedHtmlMessage react-intl v4 - reactjs

After upgrading to react-intl#4, we are facing a problem with all formattedHtmlMessages which have been used as html tag.
<FormattedHTMLMessage id={`${page}.legalNotice`} />
We tried replacing by formattedMessage and tried values={{...}}
<FormattedMessage id={`${page}.legalNotice`} />
But first of all the html is not considered as html tags.
Also as we use dynamic translation with different and undefined number of 'href's or even different 'target's on tags(links) it does not seem to be the solution.
Our translations are something similar to this:
"myPage1.legalNotice" : "By clicking here I accept <a target="_self" title="my specific title" href='first_link.pdf'>LINK1</a>, and <a target="_blank" title="second_title" href='second_link'>LINK2</a> and <a target="_blank" href='third_link' title='third_title'>LINK3</a>."
"myPage2.legalNotice" : "Another Text .. <a target="_blank" href='another_link.pdf'>LINK2</a>."

Check the Migration guide.
https://formatjs.io/docs/react-intl/upgrade-guide-3x
You have to provide the description for all the tags you use inside the message.
<FormattedMessage
defaultMessage="To buy a shoe, <a>visit our website</a> and <cta>eat a shoe</cta>"
values={{
a: msg => (
<a class="external_link" target="_blank" href="https://www.shoe.com/">
{msg}
</a>
),
cta: msg => <strong class="important">{msg}</strong>,
}}
/>
Update: If you need to pass custom variables inside the variable
Assuming you have customHref or customTarget variables defined:
a: msg => (
<a class="external_link" target="${customTarget}" href="${customHref}">
{msg}
</a>
)

To keep it the old way you can use such hack:
let message = intl.formatMessage({ id });
if (values) {
Object.entries(values).forEach(([key, value]) => {
message = message.replace(`{${key}}`, value.toString());
});
}
return(
<span
dangerouslySetInnerHTML={{ __html: formattedMessage }}
/>
)

Related

How can we add multiple classes to the `ReactMarkdown` while rendering

I my react app, I am using react markdown for rendering the blog content. How can we add multiple classes to the ReactMarkdown. At the moment I have used dataArea class alone, but i would need to use below classes for styling purpose. Could someone please advise ?
.blogImageSection
.dataArea
.dataDate
.tags
.readmoreLink
.dataArea p
. views
csb link for reference : https://codesandbox.io/s/great-bardeen-9lsog5?file=/src/App.js
// react markdown usage:
{ popularResults.map (({id, blogdetails, tags, createdAt }) => (
<ReactMarkdown className='dataArea' remarkPlugins={[remarkGfm]}>{blogdetails}
</ReactMarkdown>
))}
This is the normal blog where I have used the above classes without using react markdown
<a key={id} href="my url">
<div key={id} className='blogImageSection'>
<img alt="id" src={photo} />
<div key={id} className='dataArea'>
<span key={id} className='dataDate'>{date}</span>
<span className='tags'>cypress</span>
<h3>{heading}</h3>
<p>
Best heading added here.
The most relevant data added here.
Greatest of all time. Print the whole text here.
Ideas are always usefull....
</p>
<a href="_blank" className="readmoreLink">
Read more →
</a>
<span className='views'>
{topViews > 999 ? (topViews / 1000).toFixed(2) + "K" : topViews}
</span>
</div>
</div>
</a>
you can add all classes like this:
className='dataArea blogImageSection dataArea dataDate tags readmoreLink dataArea views'

Ignore an element inside <Link></Link> to not navigate on click

I'm trying to get to work a delete btn which is inside a div which is wrapped in react-router-dom tag. I want to be able to navigate to the established path when that div is clicked. But if the target is that delete btn, then it shouldn't navigate just execute the onClick inside the delete btn. Is this possible? I couldn't figure out on my own. Thanks for reading.
<ul className="m-entries">
{
letters.map(letter => {
return (
<li className="o-entry" key={ letter.id }>
<Link to={`/Letters/letter/${ letter.id }`}>
<div className="l-entry-container">
<div className="l-entry__header">
<h2 className="c-text__h2 c-letter-title">{ letter.title }</h2>
// This is the delete btn
<button
className="c-entry__btn c-entry__dlt-btn"
onClick={(e) => {
displayConfirmDeleteWdw(e, letter.id)
}}
type="button"
>
<XDeleteBtn />
</button>
</div>
<p className="c-text__p c-letter__preview">{ letter.synopsis }</p>
</div>
</Link>
</li>
)})
}
</ul>
It's generally considered bad UI/UX to place interactive DOM elements within other interactive DOM elements (in fact some elements are sometimes illegal when used like this), but there is a way to accomplish this.
The button's onClick event should not propagate up to the Link component. Call stopPropagation on the event to prevent it from bubbling up further.
<ul className="m-entries">
{letters.map(letter => {
return (
<li className="o-entry" key={letter.id}>
<Link to={`/Letters/letter/${letter.id}`}>
<div className="l-entry-container">
<div className="l-entry__header">
<h2 className="c-text__h2 c-letter-title">{letter.title}</h2>
<button
className="c-entry__btn c-entry__dlt-btn"
onClick={(e) => {
e.stopPropagation(); // <-- prevent event from bubbling up
displayConfirmDeleteWdw(e, letter.id);
}}
type="button"
>
<XDeleteBtn />
</button>
</div>
<p className="c-text__p c-letter__preview">{letter.synopsis}</p>
</div>
</Link>
</li>
)})
}
</ul>

React Hook not opening form onClick

I am trying to refactor from class based to functional. While doing so I will need to use hooks instead of setting this.state etc.. I am trying to get a FORM to open when i click a button. The button will also change from "add reply" to "submit comment" once the form opens. I am stumped. This is the best thing I could come up with... Doesnt work. in fact, it makes my "add reply" button completely disappear. Any thoughts on this? Here is the code that I have written. inside of the comment I am trying to return a component using ternary....
image of component as-is
import React, {useState} from 'react';
import FormOpen from './FormOpen';
const CommentCreated = (props) => {
const [resource, setResource] = useState([{visible: false, addReply: 'Add Reply'}]);
return (
<div className="ui threaded comments">
<h3 className="ui dividing header">Comments</h3>
<div className="comment">
<a href="/" className="avatar">
<img alt="avatar" src= {props.avatar} />
</a>
<div className="content">
<a href="/" className="author">{props.author}
</a>
<div className="metadata">
<span className="date">Today at 5:42PM</span>
</div>
<div className="text">{props.content}
</div>
<div className="actions">
{resource.visible ? <FormOpen /> : null}
<a className="reply" onClick={() => {setResource([{visible: true, addReply: 'Submit Comment'}]);}}>{resource.addReply}
</a>
<a className="save">Save</a>
<a className="hide">Hide</a>
</div>
</div>
</div>
</div>
);
};
export default CommentCreated;
You should replace the setResource(['Submit Comment', true]) with :
setResource([{
visible: true,
addReply: 'Submit Comment'
}])
To match the shape of your state.
EDIT:
Here is a working example based on your code.
As I can see from your example, your resource state doesn't need to be an array but simply an object.
Furthermore, as you are using hyperlinks <a />, you must use preventDefault() on your event being triggered if you don't want the page to refresh.

JSX not returning expected HTML

I have the following code in my React component's return statement:
return (
<div>
{
props.photos.length > 0 &&
<div>
{props.photos.map((photo) =>
<div>
<a target="_blank"
href="/api/game_theory/game-files/{this.props.profileId}/files/{photo.id}/{photo.downloadName}">
{photo.title}
</a>
</div>
)}
</div>
}
</div>
);
It renders without errors, however the anchor tag looks like this in the HTML:
<a target="_blank" href="/api/game_theory/game-files/{this.props.profileId}/files/{photo.id}/{photo.downloadName}">
ActualPhotoName.jpg
</a>
So photo.title is being written out correctly, but photo.id, photo.downloadName, and this.props.profileId are not.
I'm sure I'm doing something wrong and I'd appreciate any help. :)
You can't "double-interpret" javascript with brackets in JSX. Assuming your props are available to you, try using ES6 string interpolation:
href=`/api/game_theory/game-files/${this.props.profileId}/files/${photo.id}/${photo.downloadName}`>
Wrap the whole href inside { } and remove it from the props:
href={"/api/game_theory/game-files/" + this.props.profileId + "/files/" + photo.id + "/photo.downloadName"}

React anchor link delims not parsing data

I have a simple anchor / for loop:
<ul className="user-list">
{this.props.partymembers.map((user, i) => {
return (
<li key={i} className="membername">
{user.username}
</li>
)
})}
</ul>
But the link just shows up as /friends/add/{user.id} instead of /friends/add/141513531 am I missing something here? Thanks. Username shows fine, but not inside the ""
I managed to solve it by doing
<li key={i} className="membername">
{React.createElement('a', { href: '/friends/add'+user.id}, user.username)}
</li>
but that almost seems hacky, is there a better way to do it?
The whole attribute needs to be either a string or an expression. So something like this:
<a href={'/friends/add/' + user.id}>{user.username}</a>
If you're using babel to transpile ES6, you can use template strings:
<a href={`/friends/add/${user.id}`>{user.username}</a>

Resources