I am trying to get a script embedding onto my next.js application, however it is at the end of the page, instead of where I place it.
I already took the solution mentioned in How to precisely control where script tag is inserted using Next Script, but the issue still persists. I also tried to embed it with the application script method described here https://nextjs.org/docs/basic-features/script, but I get the same result.
My helloworld.tsx in pages looks like this:
import React, { useRef } from 'react'
import Script from 'next/script'
export const HelloWorld = () => {
const containerRef = useRef(null)
function moveScript() {
containerRef.current.appendChild(this)
}
return (
<>
<p>This goes before the embedding</p>
<div ref={containerRef} id="script-container">
<Script strategy="lazyOnload" id="asciicast-aMFGH8jU7O7uo94YZyWJEZtnO" type="text/javascript" src="https://asciinema.org/a/aMFGH8jU7O7uo94YZyWJEZtnO.js" async onLoad={moveScript}/>
</div>
<p>This goes after the embedding <br /></p>
</>
);
};
export default HelloWorld;
On the resulting page the embedding is at the bottom of the page, however I expect it to be between the two paragraph.
What am I missing?
Why don't you use an useEffect to load the script, and then append it?
Since useEffect runs after the render of the component, you could either:
[1] use it to load the script in it (assuring that the script load goes after the component rendering), or
[2] use it afterwards to move (append) the script to the specific ref. I can't test your code right now, but I'm pretty sure that might help you solve the problem you're having.
I have been building out a static site with gatsby and a few sections I have rendered out pages written as MDX and parsed through their own layouts. This is all via the gatsby-plugin-mdx file and it works great.
However, I'm trying to get the top-level navigation to highlight as active when a user navigates to a sub-page in that section. I am using the code from the Gatsby docs and it works on the pages I created as normal JS files.
Example:
<Link partiallyActive={true} activeClassName="header-nav-active" to={menu.url} title={menu.title}>
{menu.label}
</Link>
It seems that it doesn't seem to work for the MDX pages, even though what is rendered in location.pathname is the same. My current structure is:
src
-pages
--section
----section-subpage.js
--other section
----other-section-sub
-----index.mdx
----other-section-sub-2
-----index.mdx
Ultimately if you look at this layout I would like "Figma" to be highlighted as active when you are navigating though the sub pages in that section.
Have you tried using getProps helper function? Because Gatsby's router extends from React's (#reach/router) you can take advantage of the advanced props to customize your style
You can create a partiallyActive link like:
const isPartiallyActive = ({ isPartiallyCurrent }) => {
return isPartiallyCurrent
? { className: 'navlink-active navlink' }
: { className: 'navlink' }
}
const PartialNavLink = props => (
<Link getProps={isPartiallyActive} {...props}>
{props.children}
</Link>
)
And then simply use:
<PartialNavLink to="/figma">Figma</PartialNavLink>
Or in an unrefined way:
<Link getProps={({ isPartiallyCurrent }) => isPartiallyCurrent ? { className: "active" } : null } to={"/figma"}>
Figma
</Link>
I have a function that grabs an element and adds the class "is-open" in order to toggle an accordion. I have put this function in my "main.js".
In my FAQ component, this function needs to be called when a user clicks the accordion bar.
My question is: How do I import this main.js file within my Gatsby.js / React project?
I tried in the index or the component itself. But it is only called once I make changes to the main.js file. Obviously, I want it to be called right away when the page loads up.
This is the function within my main.js file:
const accordionEl = document.getElementsByClassName("faq-module--accordion-title--2zVOe")
if (accordionEl.length) {
for (let i = 0; i < accordionEl.length; i++) {
accordionEl[i].addEventListener("click", function() {
this.parentNode.classList.toggle("is-open")
const panel = this.nextElementSibling
if (panel.style.maxHeight) {
panel.style.maxHeight = null
} else {
panel.style.maxHeight = `${panel.scrollHeight}px`
}
})
}
}
Like I said, I have tried to add it like so in my FAQ component:
import "../js/main..js"
I have also tried to add it in index.html with tags.
You can put this in your footer (or some place that always loads):
<script src={`path/to/main.js`}></script>
Use the path to the file of the BUILT version. So you should probably put the script in "static/js/", and you should be able to import it from there.
But this is not really how you want to use Gatsby. Gatsby is built upon React, so for the reactive JavaScript, you should really be using React. It can't predict what the state of the DOM is if you start updating it behind its back. Furthermore this should be really easy to recreate in React with a simple onClick handler.
When a user changes the theme, I use mainWindow.webContents.send to change a class in the DOM. I also save it in the store, under the key theme.
mainWindow.webContents.send('theme:change', theme);
store.set('theme', theme);
Then in renderer.js:
ipcRenderer.on('theme:change', (event, theme) => {
document.querySelector('body').className = `${theme}`;
});
This successfully changes the theme and saves it in the store. However, now I want that theme to load up when starting the application rather than going back to the default. To do this, in app.on('ready') I am doing this:
mainWindow.webContents.send('theme:change', store.get('theme'));
However, nothing is happening. It's like it isn't being sent. Where am I going wrong? Essentially what needs to be done is for the class in body to be changed when the application loads to the one in the store.
Figured it out. I had to put:
mainWindow.webContents.once('dom-ready', () => {
mainWindow.webContents.send('theme:change', store.get('theme'));
})
I was trying mainWindow.on('dom-ready') which is why it wasn't working.
How do I fix this warning in console of a React app using the react-modal package:
Warning: react-modal: App element is not defined. Please use Modal.setAppElement(el) or set appElement={el}
I have not been successful at figuring out what el is supposed to be.
Context:
in my App.js root component file:
...
import Modal from 'react-modal';
...
class App extends Component {
...
render(){
...
<Modal
className="modal"
overlayClassName="overlay"
isOpen={foodModalOpen}
onRequestClose={this.closeFoodModal}
contentLabel="Modal"
>
...
}
}
Where ... indicates code not shown.
Everything works fine, but when the Modal is opened, the following Warning appears in my console:
index.js:2177 Warning: react-modal: App element is not defined. Please use Modal.setAppElement(el) or set appElement={el}. This is needed so screen readers don't see main content when modal is opened. It is not recommended, but you can opt-out by setting ariaHideApp={false}.
In the react-modal docs all I can find is the following:
App Element
The app element allows you to specify the portion of your app that should be hidden (via aria-hidden) to prevent assistive technologies such as screenreaders from reading content outside of the content of your modal.
If you are doing server-side rendering, you should use this property.
It can be specified in the following ways:
DOMElement
Modal.setAppElement(appElement);
query selector - uses the first element found if you pass in a class.
Modal.setAppElement('#your-app-element');
Unfortunately, this has not helped! I cannot figure out what el is supposed to represent.
Here are some of the many property variations I have tried adding to my Modal component:
`appElement={el}`,
`appElement="root"` where `root` is the id that my App component is injected into
`appElement={'root'}`
`appElement="div"`,
`appElement={<div>}`,
`appElement={"div"}`
I've also tried calling Modal.setAppElement('root'); from inside src/index.js, where root is the root element that my App component is injected into, and index.js is where I do that.
Add ariaHideApp={false} to Modal attributes.
This should work:
<Modal isOpen={!!props.selectedOption}
onRequestClose={props.clearSelectedOption}
ariaHideApp={false}
contentLabel="Selected Option"
>
</Modal>
Some solutions are given in react-modal issue #133:
The problem lies here:
Depending on when it evaluates react-modal#1.6.5:/lib/helpers/ariaAppHider.js#L1:
document.body does not exist yet and it will resolve to undefined || null.
if Modal.setAppElement() is called with null or not called at all with the <script /> placed on <head /> (same as above).
Probably it can also happen if called with a selector that does not match any results.
Solutions:
Browser Rendering:
#yachaka snippet prevents this behavior by defining the element before placing the <Modal />:
componentWillMount() {
Modal.setAppElement('body');
}
#ungoldman answer, if you don't want to depend on `setAppElement':
Inject the bundled application JS into <body> instead of <head>.
Though ideally react-modal should wait until the DOM is loaded to try attaching to document.body.
server-side:
If rendering on server-side, you must provide a document.body, before requiring the modal script (perhaps it should be preferable to use setAppElement() in this case).
Update:
react docs have been updated to include the information above, so they should now be clearer for users running into this issue.
react-modal issue #567: add information (from issue #133 linked above) to the docs.
Just include appElement={document.getElementById('app')} inside your modal like this
<Modal
className="modal"
appElement={document.getElementById('app')}
>
It will work 100% if app is your central in index.html from where react loads.
This is my TypeScript Modal component which wraps react-modal v3.8.1:
import React from 'react'
import ReactModal from 'react-modal'
interface Props {
isOpen: boolean
ariaLabel?: string
}
const Modal: React.FC<Props> = ({
children,
ariaLabel = 'Alert Modal',
isOpen,
}) => (
<ReactModal
appElement={document.getElementById('root') as HTMLElement}
ariaHideApp={process.env.NODE_ENV !== 'test'}
isOpen={isOpen}
contentLabel={ariaLabel}
testId="modal-content"
>
{children}
</ReactModal>
)
export default Modal
Usage in component with state = { isOpen: true }:
<Modal isOpen={this.state.isOpen}>
<p>
Modal Content hereā¦
</p>
<button onClick={() => { this.setState({ isOpen: false }) }}>Okay</button>
</Modal>
If getting the Warning: react-modal: App element is not defined... error when running tests (we were running Jest), you can suppress the warnings by adding the following to your test file:
import ReactModal from 'react-modal';
ReactModal.setAppElement('*'); // suppresses modal-related test warnings.
The shortest solution is to add
appElement={document.getElementById("hereIsYourRootElementId")}
It lets react-modal know where is your root element.
For reference, since it was a pain for me, if you are doing SSR, use the following code to prevent errors server-side:
if (typeof(window) !== 'undefined') {
ReactModal.setAppElement('body')
}
You could put this in componentDidMount() anywhere you use a modal or I put it in a custom modal component so it's nice and DRY.
Just put this
Modal.setAppElement('#root')
This will solve the warning. The root element coming from inside public folder index.html.
you need to add # before your root element id.
import React from 'react';
import Modal from 'react-modal';
Modal.setAppElement('#root');
const OptionModal = (props) => (
<Modal
isOpen={!!props.selectedOption}
contentLabel='this is the selected option'
>
<h3>Selected Option</h3>
{props.selectedOption && <p>{props.selectedOption}</p>}
<button onClick = {props.handleCloseOptionModal}>Close</button>
</Modal>
);
export default OptionModal;
here is the reference:
http://reactcommunity.org/react-modal/accessibility/
If you get that warning on testing with the "react-testing-library" here is a solution:
https://github.com/reactjs/react-modal/issues/576#issuecomment-524644035
using the react-testing-library (https://testing-library.com/) I get rid of that warning with:
import Modal from "react-modal";
const { container } = render(<MyComponent />);
Modal.setAppElement(container);
.... // to the testing, use Modal
or, if you want to test the modal component directly:
const { container, rerender } render(<MyModalComponent isOpen={false} />);
Modal.setAppElement(container);
// now the appElement is set we can show the modal component
rerender(<MyModalComponent isOpen={false} />);
.... // to the testing
For Nextjs, I think you can solve this by adding the below to outside your modal component, maybe on top, before the component is declared.
Modal.setAppElement('#__next')
Delete this attrib
className="modal"
and run again