The following code renders a warning message conditionally, when user switch site (by clicking the button, or accessing directly using URL), the website will show a warning message, and disappear on refresh.
Things work fine within the docusaurus local development server, but behaves differently once built into a production static site.
import Cookies from 'js-cookie';
import React from 'react';
import { DocProvider } from '#docusaurus/theme-common/internal';
import { HtmlClassNameProvider } from '#docusaurus/theme-common';
import { translate } from '#docusaurus/Translate';
import CommunityLinkGroup from "#site/src/components/LinkGroup";
import DocItemLayout from '#theme/DocItem/Layout';
import DocItemMetadata from '#theme/DocItem/Metadata';
import styles from "./index.module.css";
export default function DocItem(props) {
const docHtmlClassName = `docs-doc-id-${props.content.metadata.unversionedId}`;
const MDXComponent = props.content;
const url = props.location.pathname;
const prevEdition = Cookies.get('doc_edition');
var displayAlert = false;
if (url.includes('/community/')) {
Cookies.set('doc_edition', 'community');
if (prevEdition == 'cloud') {
displayAlert = true;
}
} else if (url.includes('/cloud/')) {
Cookies.set('doc_edition', 'cloud');
if (prevEdition == 'community') {
displayAlert = true;
}
}
var alertMsg = "bad msg";
var alertClass = "badcls";
var alertRole = "badrole";
if (displayAlert == true) {
alertMsg = translate({
id: 'theme.DocItem.switchDocAlertMsg',
message: 'you just switched site, please notice.',
});
alertClass = "alert alert--warning";
alertRole = "alert";
}
console.log(alertMsg, alertClass, alertRole);
return (
<DocProvider content={props.content}>
<HtmlClassNameProvider className={docHtmlClassName}>
<div className={alertClass} role={alertRole}>{alertMsg}</div>
<DocItemMetadata />
<DocItemLayout>
<MDXComponent />
</DocItemLayout>
<div className={styles.communityLinkContainer}>
<CommunityLinkGroup />
</div>
</HtmlClassNameProvider>
</DocProvider>
);
}
Works fine inside the local dev server:
But once built into a production static site, it yields impossible rendering result:
Meanwhile, production site console output shows you just switched site, please notice. alert alert--warning alert, which clearly indicates alertMsg=='you just switched site, please notice.', alertClass=="alert alert--warning", alertRole=="alert", meaning that displayAlert must be true.
But as shown in above screenshot, it looks like as if displayAlert is both false and true at the same time, a totally impossible DOM state.
Also, this only happens when accessed directly using URL path, if I click on the button provided by the website to change site, the website warning message will display normally through dynamic rendering.
I should be using state variables, like https://stackblitz.com/edit/react-pcagrw?file=src/App.js, consider this a React 101.
Related
I was trying to determined UserAgent and Retina info inside Nuxt application. But the application is throwing an error and showing navigatior / window is undefined. How can i get these info inside nuxt application?
const userAgent = navigator.userAgent.toLowerCase()
const isAndroid = userAgent.includes('android')
isRetina() {
let mediaQuery
if (typeof window !== 'undefined' && window !== null) {
mediaQuery =
'(-webkit-min-device-pixel-ratio: 1.25), (min--moz-device-pixel-ratio: 1.25), (-o-min-device-pixel-ratio: 5/4), (min-resolution: 1.25dppx)'
if (window.devicePixelRatio > 1.25) {
return true
}
if (window.matchMedia && window.matchMedia(mediaQuery).matches) {
return true
}
}
return false
}
This is the solution to fix:
navigator is undefined
window is undefined
document is not defined
Here is an example on how you should wrap your logic JS code
<script>
import { jsPlumb } from 'jsplumb' // client-side library only, no SSR support
export default {
mounted() {
if (process.client) {
// your JS code here like >> jsPlumb.ready(function () {})
}
},
}
</script>
As shown here: https://nuxtjs.org/docs/2.x/internals-glossary/context
PS: mounted + process.client are kinda redundant because mounted only runs on the client.
Also, wrapping your component into <client-only> if you want it to be only client side rendered is also a good idea.
<template>
<div>
<p>this will be rendered on both: server + client</p>
<client-only>
<p>this one will only be rendered on client</p>
</client-only>
<div>
</template>
The documentation for this one is here: https://nuxtjs.org/docs/2.x/features/nuxt-components/#the-client-only-component
PS: beware because this client-only tag will only skip the rendering, not the execution, as explained here.
Some packages do not support SSR when you import them, for that you could either:
use a condition with a dynamic import (if the library is meant to be used later on)
directly like this (if you want to load a component like vue-editor)
export default {
components: {
[process.client && 'VueEditor']: () => import('vue2-editor'),
}
}
i have defined an image in spinner.js like so:
import React from 'react';
import broomAndText from './mygif.gif';
function myGif() {
return <img src={broomAndText} alt="broomAndText" width="200px" height="200px" />
}
export default myGif;
i then want to call the image, and display it on page in my mainPage.js. Here's how I am trying to call it:
import myGif from './spinner'
var regData = ""
firestore.collection("profiledata").doc(user.uid).get().then((doc) => {
var firstName = doc.data().firstname;
var lastName = doc.data().lastname;
var companyName = doc.data().companyname;
var email = doc.data().email;
console.log(doc.data().registeringFlag);
regData = doc.data().registeringFlag;
console.log("reg data " + regData)
if (regData == "yes"){
regData = {myGif}
}
}).then(() => {
if (regData != "no") {
document.write(regData)
}
})
what am I doing wrong? It is just outputting [object Object]
I think what You need is to render your react element properly.
Instead of just writing text into html via document.write() You need to render it to some existing DOM element. For instance, a div inside a body:
import ReactDOM from 'react-dom'
import MyGif from './spinner'
const div = document.createElement('div')
div.id = '__root'
document.body.appendChild(div)
ReactDOM.render(<MyGif/>, document.getElementById('__root'))
Also please note that I replace your camelCase notation for a myGif with PascalCase, as react will interpret it incorrectly the other way.
You also need to put your element inside xml tag (<> braces) to actually create it and make it work.
As first step You may just replace document.write(regData) with ReactDOM.render(myGif, document.getElementsByTagName('body') and it should work.
But in perspective it would be much better to check some basic react tutorials on how the rendering process works in react. It is not as simple as putting plain html into document.
We are moving from GEB/Spock to Cypress for front end automation. With GEB/Spock, the page objects had static content. This was used to create the selectors for the page objects.
class LoginPage extends Page {
//private static Logger log = LoggerFactory.getLogger(LoginPage.class);
static url = 'login/'
static at = { title == "Login to TalentBank"}
static content = {
backgroundImage { $("div [style*=\"background-image\"]") }
logo { $(".center-img img") }
emailHeader { $("#emailGroup:not([style*=\"none\"]) label") }
emailTextBox { $('#emailGroup:not([style*="none"]) #email') }
nextButton { $('#loginWithEmail') }
pwdHeader { $("#passwordGroup:not([style*=\"none\"]) label") }
pwdTextBox { $("#passwordGroup:not([style*=\"none\"]) #password") }
loginButton { $("#loginWithPassword") }
loginError(wait: true) { $("#loginError") }
In cypress, I'm unsure where to create and call these objects. Are the selectors supposed to be created as fixtures or as support files? I've read through the cypress documentation, but can't find what I am looking for.
Edit: 2/4
Under support, I tried creating a LoginPage.js file to contain the objects
// Login Page Objects
const emailTextBox = $('#emailGroup:not([style*="none"]) #email');
My test is under integration directory. I use the new constant in my test. There are no errors in my IDE, as the test appears to find the constant as it displays in the Ctrl+Space code completions.
describe('Successfull log in to the System', function() {
it('Standard User - Successful log in ', function() {
cy.get(emailTextBox).type('RodneyRuxin#mailinator.com')
When I run my test however, I get an error that says
ReferenceError: emailTextBox is not defined
.
Okay so these are different selectors to interact with:
I would recommend a completely different class and then do an import of them?
So for example a file called locators.js
which contains:
export const backgroundImage = () => cy.get("div [style*=\"background-image\"]");
then in your other file, you can import it like so:
import * as locators from "../locators/locators.js";
and call it like this (example):
locators.backgroundImage()
.should('be.visible')
.click();
Hope this helps!
I'm displaying text that was stored in the database. The data is coming from firebase as a string (with newline breaks included). To make it display as HTML, I originally did the following:
<p className="term-definition"
dangerouslySetInnerHTML={{__html: (definition.definition) ? definition.definition.replace(/(?:\r\n|\r|\n)/g, '<br />') : ''}}></p>
This worked great. However there's one additional feature. Users can type [word] and that word will become linked. In order to accomplish this, I created the following function:
parseDefinitionText(text){
text = text.replace(/(?:\r\n|\r|\n)/g, '<br />');
text = text.replace(/\[([A-Za-z0-9'\-\s]+)\]/, function(match, word){
// Convert it to a permalink
return (<Link to={'/terms/' + this.permalink(word) + '/1'}>{word}</Link>);
}.bind(this));
return text;
},
I left out the this.permalink method as it's not relevant. As you can see, I'm attempting to return a <Link> component that was imported from react-router.However since it's raw HTML, dangerouslySetInnerHTML no longer works properly.
So I'm kind of stuck at this point. What can I do to both format the inner text and also create a link?
You could split the text into an array of Links + strings like so:
import {Link} from 'react-router';
const paragraphWithLinks = ({markdown}) => {
const linkRegex = /\[([\w\s-']+)\]/g;
const children = _.chain(
markdown.split(linkRegex) // get the text between links
).zip(
markdown.match(linkRegex).map( // get the links
word => <Link to={`/terms/${permalink(word)}/1`}>{word}</Link> // and convert them
)
).flatten().thru( // merge them
v => v.slice(0, -1) // remove the last element (undefined b/c arrays are different sizes)
).value();
return <p className='term-definition'>{children}</p>;
};
The best thing about this approach is removing the need to use dangerouslySetInnerHTML. Using it is generally an extremely bad idea as you're potentially creating an XSS vulnerability. That may enable hackers to, for example, steal login credentials from your users.
In most cases you do not need to use dangerouslySetHTML. The obvious exception is for integration w/ a 3rd party library, which should still be considered carefully.
I ran into a similar situation, however the accepted solution wasn't a viable option for me.
I got this working with react-dom in a fairly crude way. I set the component up to listen for click events and if the click had the class of react-router-link. When this happened, if the item has a data-url property set it uses browserHistory.push. I'm currently using an isomorphic app, and these click events don't make sense for the server generation, so I only set these events conditionally.
Here's the code I used:
import React from 'react';
import _ from 'lodash';
import { browserHistory } from 'react-router'
export default class PostBody extends React.Component {
componentDidMount() {
if(! global.__SERVER__) {
this.listener = this.handleClick.bind(this);
window.addEventListener('click', this.listener);
}
}
componentDidUnmount() {
if(! global.__SERVER__) {
window.removeEventListener("scroll", this.listener);
}
}
handleClick(e) {
if(_.includes(e.target.classList, "react-router-link")) {
window.removeEventListener("click", this.listener);
browserHistory.push(e.target.getAttribute("data-url"));
}
}
render() {
function createMarkup(html) { return {__html: html}; };
return (
<div className="col-xs-10 col-xs-offset-1 col-md-6 col-md-offset-3 col-lg-8 col-lg-offset-2 post-body">
<div dangerouslySetInnerHTML={createMarkup(this.props.postBody)} />
</div>
);
}
}
Hope this helps out!
I wanted to hide some issue link outward & inwards strings of Link type from the Link Issues Popup Window using java script.
I have tried using java script but I am not getting the popup screen from the java script.
Please see the screenshot below :
Can anyone tell me how can I get this popup screen in the java script?
Is there any other method to hide this?
Thanks & Regards,
Renuka.
To hide the clone issue link every page:
edit the file system-webresources-plugin.xml (should be at /atlassian-jira/WEB-INF/classes/), and add to <web-resource key="jira-fields"> this code:
<resource type="download" name="myScript.js" location="/includes/jira/field/script.js">
<param name="source" value="webContextStatic"/>
</resource>
than, on /includes/jira/field/myScript.js write this:
AJS.$(document).ready(function() {
if (AJS.$("#link-type option[value*='clon']").size() > 0) {
// will work even when right clicking on More
// Actions->Link & open it into a new window
AJS.$("#link-type option[value*='clon']").remove()
} else if (AJS.$("#link-issue").size() > 0) {
// will work in case the link menu showing via popup
AJS.$("#link-issue").click(function(){
// wait for the popup to show, and remove the clone options
setTimeout(function (){
AJS.$("#link-type option[value*='clon']").remove();
}, 300);
});
}
});
restart Jira and it that it!
The script attaches a function to the link-menu opening, than gives the menu 0.3 seconds to load, and removes the unwanted items. If it doesn't work well for you, try to raise the timeout from 300 to 500-1000.
On jira 4, run instead:
AJS.$("#issue-link-link-type option[value*='clon']").remove();
The previous solution has an issue:
It will only work when clicking the "Link Issue"-Menu-Item.
When I use the Point (.)-Shortcut-Menu, it won't remove the issue types.
I have established the following solution:
JS-Binding-Part:
AJS.$(document).ready(function() {
JIRA.bind(JIRA.Events.NEW_CONTENT_ADDED, function(e, context, reason) {
hideIssueLinkTypes();
});
});
JS-Backing-Function:
function hideIssueLinkTypes() {
var apiURL = "/rest/scriptrunner/latest/custom/getHiddenLinkTypes"
$.getJSON( apiURL, {
}).done(function( objectData ) {
$.each( objectData, function( i, item ) {
var issueLinkType = item.issueLinkType[0];
AJS.$("#link-type option[value='"+issueLinkType.inwardDescription+"']").remove();
AJS.$("#link-type option[value='"+issueLinkType.outwardDescription+"']").remove();
});
});
}
Scriptrunner-REST-Endpoint:
import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import groovy.json.JsonBuilder
import groovy.transform.BaseScript
import com.atlassian.jira.issue.link.DefaultIssueLinkTypeManager
import com.atlassian.jira.issue.link.IssueLinkTypeManager
import com.atlassian.jira.issue.link.IssueLinkType
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.config.properties.ApplicationProperties
import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response
#BaseScript CustomEndpointDelegate delegate
String HIDDEN_IDENT="[hidden]"
getHiddenLinkTypes(httpMethod: "GET") { MultivaluedMap queryParams, String body ->
def appProperties = ((ApplicationProperties) ComponentAccessor.getComponentOfType(ApplicationProperties.class));
def appClonersLinkTypeName = appProperties.getDefaultBackedText("jira.clone.linktype.name");
def jsBuilder=new JsonBuilder();
def issueLinkTypes = ((IssueLinkTypeManager) ComponentAccessor.getComponentOfType(IssueLinkTypeManager.class)).getIssueLinkTypes();
jsBuilder issueLinkTypes.findAll({it.getName().contains(HIDDEN_IDENT) || it.getName()==appClonersLinkTypeName }),
{ IssueLinkType linkType ->
issueLinkType linkType.getId(),
name: linkType.getName(),
inwardDescription: linkType.getInward(),
outwardDescription: linkType.getOutward()
}
return Response.ok(jsBuilder.toString()).build();
}
What you can do then ist just annotate and Link-Type with putting [hidden] in the link name and it will disappear for all users (It can still be programmatically added though or created by cloning).
If you don't have Scriptrunner or don't need the dynamic nature of the implementation, you can still hard-code the values as Kuf described in the answer above in hideIssueTypes() like this:
AJS.$("#issue-link-link-type option[value*='clon']").remove();