Render custom components in React Markdown - reactjs

I'm building my website documentation and I need to render custom component and it's props, such as <Callout>Hi! I'm a callout</Callout>.
I'm using contentlayer for the directory and react-markdown to render the content.
Here's my code.
<ReactMarkdown
rehypePlugins={[rehypeRaw]}
className={s.markdown}
components={{
code({ inline, children, ...props }) {
if (!inline) {
return (
<SyntaxHighlighter
style={coldarkDark}
language={'typescript'}
>
{String(children).replace(/\n$/, '')}
</SyntaxHighlighter>
)
}
return <code {...props}>{children}</code>
},
}}
>
{currentDoc.body}
</ReactMarkdown>
{currentDoc.body} is markdown. So, with this example, I'm passing this to <ReactMarkdown />:
Mollit nisi cillum exercitation minim officia velit laborum non Lorem
adipisicing dolore. Labore commodo consectetur commodo velit adipisicing irure
dolore dolor reprehenderit aliquip. Reprehenderit cillum mollit eiusmod
excepteur elit ipsum aute pariatur in. Cupidatat ex culpa velit culpa ad non
labore exercitation irure laborum.
<Callout>Hi! I'm a callout</Callout>
Hey! [link](https://github.com/rehypejs/rehype-react) right here

The idea is to catch your component Callout from your markdown and render it?
If so, I think i would use a custom "code language" in your markdown, and then parse it in yourcomponents callback from react-markdown.
For example, your markdown would be
Mollit nisi cillum exercitation minim officia velit laborum non Lorem
adipisicing dolore. Labore commodo consectetur commodo velit adipisicing irure
dolore dolor reprehenderit aliquip. Reprehenderit cillum mollit eiusmod
excepteur elit ipsum aute pariatur in. Cupidatat ex culpa velit culpa ad non
labore exercitation irure laborum.
```callout
Hi! I'm a callout
```
Something else....
React-markdown then render anything as code with a particular className using language-{code} which means in this case language-callout. So you need to catch it and render your callout depending on the className
code({ inline, className, children, ...props }) {
if (inline) return <code {...props}>{children}</code>
const value = String(children).replace(/\n$/, '');
if (className === "language-callout") return (
<Callout>{value}</Callout>
)
return (
<SyntaxHighlighter
style={coldarkDark}
language={'typescript'}
>
{value}
</SyntaxHighlighter>
)
}

Related

Toggle FieldSet from an other component

How to make PrimeReact FieldSet controllable from an other (parent) component on React?
I have a boolean object toggled, i would like to pass it into child component.
When toggled = true, the FieldSet should be opened.
When toggled = false, the FieldSet should be closed.
import React from "react";
import { Fieldset } from "primereact/fieldset";
const FieldsetDemo = () => {
return (
<div>
<div className="card">
<h5>Toggleable</h5>
<Fieldset legend="Header" toggleable>
<p>
Lorem ipsum dolor sit amet.
</p>
</Fieldset>
</div>
</div>
);
};
Its easy. Working code sandbox: https://codesandbox.io/s/quizzical-snow-so91tv?file=/src/demo/FieldsetDemo.js
const FieldsetDemo = (props) => {
return (
<div>
<div className="card">
<h5>Toggleable</h5>
<Fieldset legend="Header" toggleable collapsed={props.collapsed}>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.
</p>
</Fieldset>
</div>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<FieldsetDemo collapsed={true} />, rootElement);

how to render a video from contentful in gatsbyjs

My video from contentful doesn't seem to be rendering properly. It's in the DOM but I can't get it to display. Any ideas why ?
I was thinking maybe it's an incorrect path and that's why it can't access it but the image works and displays just fine. There is also an eslint error message asking for a track element, could that be blocking it?
This is the code
{data.allContentfulPodcast.edges.map(video =>
<div>
<ReactPlayer
priority
autoPlay
loop
muted
className='react-player'
url={'https:' + video.node.video.file.url}
playing={isPlaying}
height = '100%'
width = '100%'
/>
</div>
)}
and the console.log
{
"allContentfulPodcast": {
"edges": [
{
"node": {
"title": "Welcome to our show",
"thumbnail": {
"file": {
"url": "//images.ctfassets.net/hi3b2mc578jm/3wxzzKv4Rblsv1FAUR2SQ/d8f09ade7e9fc57afa2f425bb2da9ed5/business.jpg"
}
},
"content": {
"raw": "{\"data\":{},\"content\":[{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\\n\\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\\n\\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\\n\\n\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"}],\"nodeType\":\"document\"}"
},
"video": {
"file": {
"url": "//assets.ctfassets.net/hi3b2mc578jm/4kK38bwyJRxPYoM3SADQrU/4b11d4079052f651685a407c7fc6ecb4/watch"
}
},
"audioClip": {
"file": {
"url": "//assets.ctfassets.net/hi3b2mc578jm/4QlnJBILMhACqeadWfr95S/7d70e3ff7d077cf463458edd93099432/mixkit-fast-rocket-whoosh-1714.wav"
}
}
}
}
]
}
}
To me, it seems that ReactPlayer is not able to find the video because it's stored locally, not in the domain you are telling the player (https://...).
Try deploying your site to get a valid URL or use a local path, using publicUrl or localUrl instead of video.file.url.
For the audio caption, you should use the file along with config props like:
<ReactPlayer
priority
autoPlay
loop
muted
className='react-player'
url={'https:' + video.node.video.file.url}
playing={isPlaying}
height = '100%'
width = '100%'
config={{ file: {
tracks: [
{kind: 'subtitles', src: 'subs/subtitles.en.vtt', srcLang: 'en', default: true},
{kind: 'subtitles', src: 'subs/subtitles.ja.vtt', srcLang: 'ja'},
{kind: 'subtitles', src: 'subs/subtitles.de.vtt', srcLang: 'de'}
]
}}}
/>
There is also an eslint error message asking for a track element,
could that be blocking it?
Depends on your ESLint configuration. You can configure it to block or to throw an error in the compilation (error) or to warn you but allow the compilation to complete (warning). According to what you say, it seems the second option, otherwise, you wouldn't be able to compile the project.

How to prevent pre-loading of my modal when it is definied of false in localStorage?

I created a modal that opens when it has not been read yet and which no longer opens when it has been read. I have stored the value false in the localStorage and if it has been read it opens and then closes. I wish it would never open if it hasn't been read.
import React, { useEffect, useState } from "react";
import Loader from "react-loader-spinner";
export default function Modal() {
const [isOpen, setIsOpen] = useState(true);
useEffect(()=>{
const isRead = localStorage.getItem("value");
if (isRead){
setIsOpen(false)
} else if (!isRead){setIsOpen(true)};
},[])
return (
<>
{isOpen && (
<div>
<button
className="close" onClick={()=>{ setIsOpen(false) || localStorage.setItem("value", false)}}
/>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui
officia deserunt mollit anim id est laborum.
</p>
</div>
)}
{!isOpen && (
<Loader
className="spinner"
type="ThreeDots"
color="#F5F5F5"
height={100}
width={100}
timeout={1500}
/>
)}
</>
);
}
I have resolved my problem :
import React, { useEffect, useState } from "react";
import Loader from "react-loader-spinner";
export default function Modal() {
const [isOpen, setIsOpen] = useState(false);
useEffect(()=>{
const isRead = localStorage.getItem("value");
if (!isRead){
setIsOpen(true)
}
},[])
return (
<>
{isOpen && (
<div>
<button
className="close" onClick={()=>{ setIsOpen(false) || localStorage.setItem("value", true)}}
/>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui
officia deserunt mollit anim id est laborum.
</p>
</div>
)}
{!isOpen && (
<Loader
className="spinner"
type="ThreeDots"
color="#F5F5F5"
height={100}
width={100}
timeout={1500}
/>
)}
</>
);
}
The first problem is on this line
else if (!isRead){setIsOpen(true)}
Once isRead returns false i.e the modal was read and then you add the not operator it'll return true and that will set setIsOpen to true which opens the modal.
You can try this:
import React, { useEffect, useState } from "react";
import Loader from "react-loader-spinner";
export default function Modal() {
const [isOpen, setIsOpen] = useState(true);
useEffect(()=>{
const isRead = localStorage.getItem("value");
if (isRead){
setIsOpen(false)
}
},[])
return (
<>
{isOpen && (
<div>
<button
className="close" onClick={()=>{ setIsOpen(false) || localStorage.setItem("value", true)}}
/>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui
officia deserunt mollit anim id est laborum.
</p>
</div>
)}
{!isOpen && (
<Loader
className="spinner"
type="ThreeDots"
color="#F5F5F5"
height={100}
width={100}
timeout={1500}
/>
)}
</>
);
}

React Native - Wrapping an app container in a ScrollView

I'm attempting to wrap the entirety of my navigation in a scrollable container.
Though I've tried, I am unable to find the right permutation of flex properties, style vs. contentContainerStyle, etc. that results in a ScrollView surrounding an app container actually being scrollable.
All is well with the following:
class Asdf extends React.Component {
render() {
return (
<View>
<Text
style={{
fontSize: 100
}}
>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</Text>
</View>
);
}
};
export default class App extends React.Component {
render() {
return (
<ScrollView
contentContainerStyle={{
flexGrow: 1
}}
>
<Asdf />
</ScrollView>
);
}
};
Rendering the navigator and its app container, though, immediately causes it to "max out" its scrolling capabilities and perform the ricochet animation on my iPhone with any kind of scroll:
class Asdf extends React.Component {
render() {
return (
<View>
<Text
style={{
fontSize: 100
}}
>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</Text>
</View>
);
}
};
const
Hjkl = createStackNavigator({
Asdf: Asdf
}),
Qwer = createAppContainer(Hjkl);
export default class App extends React.Component {
render() {
return (
<ScrollView
contentContainerStyle={{
flexGrow: 1
}}
>
<Qwer />
</ScrollView>
);
}
};
Many thanks to whoever is able to provide the probably one line of code that makes this work properly.
I tried to determine it, really.

Implementing drawer (Offcanvas) in react

I am trying to implement Drawer (Offcanvas) component in react, so here the requirement is, there will be one Drawer whose component will change dynamically, consider something like bootstrap modal, you have one modal but you can trigger that modal from anywhere from the page just write data-toggle="modal" data-target="#modalid", but implementing this in react requires communicating between 2 root components & setting drawers component data from the triggering component or button. I have seen people added trigger & modal in same component & than passing state.toggle flag like below:
render() {
return (
<div>
<Button color="danger" onClick={this.toggle}>{this.props.buttonLabel}</Button>
<Drawer isOpen={this.state.drawer} toggle={this.toggle} className={this.props.className}>
<DrawerBody>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</DrawerBody>
</Drawer>
</div>
);
}
But in my case, there is no relationship between components, this component <Button color="danger" onClick={this.toggle}>{this.props.buttonLabel}</Button> will be added in multiple places & there is no coupling between Drawer & Button. So how I can achieve this without using any state management like Redux or Flux or anything else

Resources