Make part of the text as link.(react - localization) - reactjs

I need to mark part of the text as a link. Something like:
"Please log in with your email...". This text must be localized later.
I need that "log in" part to be the link.
When I do something like this in the render method:
var link = React.DOM.a({
href: this.makeHref('login')
},
'log in'
);// or React.createElement or
//var link = <a href={this.makeHref('login')}>
// 'log in'</a>;
<div>{'Please '+ link + ' with your email...'}</div>
It will output:
Please `[object Object]` with your email...
Without surround text, I receive the expected result. In other words: How to make react render HTML not object.
This is a simplified example - I need to insert link text with format marker {0} like in C# - or any other working solution.
Thank you for help!

If you want to use an element within another element, just use curly braces like so:
var Component = React.createClass({
render: function() {
var link = <a href={this.makeHref('login')}>log in</a>;
return <div>Please {link} with your email.</div>;
}
};
You can see a working fiddle here: https://jsfiddle.net/jrunning/fencjn4x/
If you're going to be internationalizing your app at some point in the future I recommend a) crossing that bridge when you come to it, and b) using a solution like React Intl instead of trying to build your own solution with string concatenation.

Related

How to get immediate text with webdriverIO

I have html DOM like this:
<div class="alert alert-danger">
<button class="close" aria-hidden="true" data-dismiss="alert" type="button">×</button>
Your username or password was incorrect.
</div>
I would like to get Your username or password was incorrect. text.
If I do:
$('.global-alerts div.alert-danger').getText()
Then I get this ×.
Is there a way to get the only text part inside that div element?
I managed to do something like this:
getErrorMessageText() {
return browser.execute(function () {
const selector = '.global-alerts div.alert-danger';
// #ts-ignore
return $(selector).contents().not($(selector).children()).text().trim();
});
}
And it works.
But does anybody have better idea? Or more of like webdriverIO approach here?
Does it work if you use something like this?
var innerHTML = $('.global-alerts div.alert-danger').getHTML(false);
the false argument tells indicates whether or not to include the selector element tag in the output.
Serious solution
I do not quite see any other way but to use execute in order to "grab" that information from the page.
I would however place it in a browser command (either do it in the config "before" hook, or add a service that adds the command in the before hook)
This is what I ended up with considering typescript as main language, ignoring the use of jQuery, and considering that you use the before hook:
/**
* Gets executed before test execution begins. At this point you can access to all global
* variables like `browser`. It is the perfect place to define custom commands.
* #param {Array.<Object>} capabilities list of capabilities details
* #param {Array.<String>} specs List of spec file paths that are to be run
* #param {Object} browser instance of created browser/device session
*/
before: function (_capabilities: any, _specs: any, browser: WebdriverIO.Browser) {
browser.addCommand(
'getNodeText',
async function() {
return this.execute(
(selector: string) =>
Array.from( document.querySelector(selector).childNodes || [])
.filter((n: HTMLElement) => n.nodeType === n.TEXT_NODE)
.map(n => n.textContent)
.reduce(function(f, c) {return f+c;}, '')
.replace('\n', '')
.trim(),
this.selector
);
},
true
);
},
With this approach, typescript might complain about the function that passed to webdriver to get executed, so you can either write it properly, or just move it to a .js file and be done with it.
Just watch for document.querySelector(selector), in theory, it should not be null since the command is executed on an already found by webdriver element.
The way you grab the text there is just await (await $('.alert.alert-danger').getNodeText());
This should return the full string from within the node itself, but not any subchild.
Note: If you end up with an element like: <div id="mytxt">my text style is <strong>strong</strong> and <italic> italic </italic>. - html fan</div> and you do this getNodeText(), you probably end up with the value my text style is and . - html fan.
The "don't get bothered to much" solution
This approach will also sort of check that the "x" button is still there.
await expect($('.global-alerts div.alert-danger')).toHaveText('xYour username or password was incorrect.')
in latest version of WebDriverIO (v8), you can use this selector: aria/YourContent. For example:
With the DOM like this:
<h1>Hello World!</h1>
You can use this selector
console.log(await $('aria/Hello World!').getText()) // outputs: "Hello World!"
Ref: https://webdriver.io/docs/selectors/#fetch-by-content

How to trigger Quill JS's Mention Module when a button is clicked

I'm using this quill mention module for my quill editor: https://github.com/afry/quill-mention.
My editor toolbar has an "#" button, and I was hoping that what if I click that the mention-list will appear.
Was thinking my code will be something like this:
$("button.mention-btn").on("click", triggerMention);
function triggerMention(){
if(!quill.hasFocus()){
quill.focus();
}
evt1 = $.Event('keydown');
evt1.which = 16; // shift
evt2 = $.Event('keypress');
evt2.which = 50; // 2
quill.trigger(evt1);
quil.trgger(evt2);
}
Can someone with quill-js experience please help. Thanks in advance.
As stated in the docs, the method openMenu(denotationChar):
Opens the mentions menu for the given denotation character.
In order to call this method, we need to retrieve the module from Quill like: quill.getModule('mention').
The following code should work:
function showMenu() {
quillEditor.getModule("mention").openMenu("#");
}
Codepen:
https://codepen.io/andreivictor/pen/yLbjPaQ

REACT - How to replace URL strings to <a> elements and rendering it properly

Given the following content:
Hi Christine,
Are you receiving?
--
Daniel Vieira Costa.
Frontend Developer | Zeuss, Inc.
Know It's Possible.
http://www.zeuss.com
I want to replace all URL (e.g. http://www.zeuss.com) to <a> elements/objects and render it within a React content.
My base code is the following:
// regular expression used to find out all URL occorrences
let regexpression = /(?:(?:https?|ftp|file):\/\/|www\.|ftp\.)(?:\([-A-Z0-9+&##\/%=~_|$?!:,.]*\)|[-A-Z0-9+&##\/%=~_|$?!:,.])*(?:\([-A-Z0-9+&##\/%=~_|$?!:,.]*\)|[A-Z0-9+&##\/%=~_|$])/ig;
// replace all URL occurrences to <a> elements
content = content.replace(regexpression, newLink => {
return <a>{newLink}</a>
})
However, the it's rendering the following result:
Hi Christine,
Are you receiving?
--
Daniel Vieira Costa.
Frontend Developer | Zeuss, Inc.
Know It's Possible.
[object Object]
Where [object Object] should be the <a> element.
I would like to avoid raw HTML with React (setting the dangerouslySetInnerHtml attribute). Is there a better implementation?
What you are creating with that syntax is a react element, what oyu want is a string though, so try:
return '<a>' + newLink + '</a>'
You will have to use dangerouslySetInnerHtml anyway as you will have a string with HMTL inside after your replace operation.
Add on answering to the comment:
You might actually get away with doing this by first splitting the string at the URLs, then put it back together in a JSX expression.
So given you manage to split the string into an array like this (should be doable but I leave that part to you):
let splitString = ['Hi Christine,
Are you receiving?
--
Daniel Vieira Costa.
Frontend Developer | Zeuss, Inc.
Know It's Possible.', 'http://www.zeuss.com']
let parsedResult = splitString.map((part) => {
if (part.matches(regexpression)) {
return <a>{part}</a>
}
return part
})
return <div>{parsedResult}</div>
This should work without using dangerouslySetInnerHtml.

JQuery.html($sce.trustAsHtml("<br>loltest")) not working

Here're video examples of what's happening:
Without sanitizing,
https://i.lithi.io/6GLS.mp4
With Sanitizing
https://i.lithi.io/TVIu.mp4
When clicked, this is supposed to make the text above, render the full text.
To do this, I have created a function
$scope.loadMore = function($event)
{
var element = $($event.currentTarget);
//console.log(element.data('text'));
var text = $scope.toHtml(element.data('text'));//$compile(element.data('text'));
//$sce.trustAsHtml(element.data('text'))
console.log(text);
if (element.parent().find('#text_data').length) {
element.parent().find('#text_data').html(text);
} else {
/*element.parent().find('#text_data').html(
element.data('text').length > 100 ?
element.data('text').substring(0, 100)
:
element.data('text')
);*/
}
}
I'm calling this function with ng-click. Basically it gets the parent, and then selects the element with the text inside.
The button has a Data Attribution with the full length text (not limited to 100 chars).
When I sanitize it inputs blank text? Whereas if I don't sanitize, it inputs the text.
I don't know if it's the right way to actually do it, but I just replaced the
tag with \n in my PHP, now it doesn't render them. Since YouTube doesn't support other HTML, it should be fine.
This is because your HTML is encoded. You have to decode it like this before displaying.
//angular hack to html decode
let decoded = angular.element('<div />').html(data).text()
$scope.decodedData = $sce.trustAsHtml(decoded);
There is nothing like $scope.toHtml(data)
Fiddle: https://jsfiddle.net/codeandcloud/54mzkjqe/

How to use Backbone.Marionette.ItemView with Mustache

The following code works fine using Backbone.Marionette.ItemView but not Mustache.
Backbone.Marionette.ItemView - no Mustache
I would like to use the same code but loading the template varaible using Mustache.
Here is my code:
Backbone.Marionette.ItemView - with Mustache
Any idea why my code does not work and why?
Thanks
I'd like to update the answer here a bit as I was just struggling with this, and I was using this answer as a reference.
Here are my findings:
The answer here is a bit out of date with the current version of Mustache (which is understandable as it's pretty old)
Mustache.to_html is now deprecated, but still exists as a simple wrapper around Mustache.render for backwards compat. Check out this link.
Additionally, I found overriding Marionette.Renderer.render, as in the accepted answer above, completely bypasses the Marionette.TemplateCache layer which may not be the desired behavior.
Here's the source for the Marionette.Renderer.render method:
render: function(template, data){
if (!template) {
var error = new Error("Cannot render the template since it's false, null or undefined.");
error.name = "TemplateNotFoundError";
throw error;
}
var templateFunc;
if (typeof template === "function"){
templateFunc = template;
} else {
templateFunc = Marionette.TemplateCache.get(template);
}
return templateFunc(data);
}
Source
As you can see it accesses the Marionette.TemplateCache.get method and the above answer does nothing to maintain that functionality.
Now to get to my solve (note: the above answer is not wrong necessarily; this is just my approach to maintain the Marionette.TemplateCache layer):
As the comments suggest above, override compileTemplate instead:
Marionette.TemplateCache.prototype.compileTemplate = function(rawTemplate) {
// Mustache.parse will not return anything useful (returns an array)
// The render function from Marionette.Renderer.render expects a function
// so instead pass a partial of Mustache.render
// with rawTemplate as the initial parameter.
// Additionally Mustache.compile no longer exists so we must use parse.
Mustache.parse(rawTemplate);
return _.partial(Mustache.render, rawTemplate);
};
Here's a working JSFiddle as proof.
In the fiddle, I've also overridden Marionette.TemplateCache.loadTemplate to demonstrate that it's only called once. The body of the function only adds some debug output and then re-implements most of the original functionality (minus error handling).
Marionette assumes the use of UnderscoreJS templates by default. Simply replacing the template configuration for a view isn't enough. You also need to replace how the rendering process works.
In your simple example, you only need to override the Marionette.Renderer.render function to call Mustache, and then set the template of your views to the string template that you want:
Backbone.Marionette.Renderer.render = function(template, data){
return Mustache.to_html(template, data);
}
var rowTemplate = '{{ username }}{{ fullname }}';
// A Grid Row
var GridRow = Backbone.Marionette.ItemView.extend({
template: rowTemplate,
tagName: "tr"
});
Note that your JSFiddle still won't work even when you put this code in place, because the GridView is still using a jQuery selector/string as the template attribute. You'll need to replace this with the same type of template function to return mustache.
http://jsfiddle.net/derickbailey/d7qDz/

Resources