Highlighting search results for markdown - reactjs

I'm new to ReactJS. I am trying to implement a search bar which should highlight the results in a page. The problem is that I can't highlight any text because the text is in markdown. I tried using SyntaxHighlighter but the examples on the web are on javascript and they are a little different (with parameter that are unknown) as I'm working with .tsx files. Is there a way to implement a search bar that highlights markdown text on the page in typescript?
Here is my component:
interface Props {
data: MyData;
searchInput: string
}
const MyClass= (props: Props) => {
return (
<div>
<ReactMarkdown>
{props.data.someText}
</ReactMarkdown>
</div>
);
};
export default MyClass;
Thank you.

Related

Gatsby GraphQL Variables In Component

I'm an iOS developer and I've been struggling for what seems like the longest time making my portfolio site from scratch. I've tried a bunch of different technologies and have finally settled on using Gatsby to create it.
So far things have been fairly straightforward but I can not figure out for the life of me how to get a component that looks like the picture below. I've gotten most of the layout design working, but I can't seem to use graphql to query the images I need in the component.
Desired Layout
I've found plenty of Gatsby example templates such as this one and this one that are similar. However the main difference is that each of these only have one image and they seem to be using Gatsby 2.0 instead of 3.0.
I can get one image using "useStaticQuery", however I need access to different images for each component. From my understanding this is not possible to do within a component, only on a page. I also can not pass the image path as a variable to StaticImage either.
export default function App(props) {
const query = useStaticQuery(graphql`
query AppSectionImages {
icon: file(relativePath: { eq: "EzMaxRequest/AppIcon_180.png" }) {
childImageSharp {
gatsbyImageData(
width: 200
placeholder: BLURRED
formats: [AUTO, WEBP, AVIF]
)
}
}
}
`);
const image = getImage(query.icon);
const app = props.app;
return (
<div>
<h1>{app.title}</h1>
<GatsbyImage image={image} />
</div>
);
Result
Can anyone please explain to me how I can get the desired layout in a component?
Edit
Here is some relevant code of what I am doing.
This is my index.js home page.
export default function IndexPage({ data }) {
const projects = data.apps.edges;
return (
<Layout>
<SEO title="Home" />
<HeroSection />
<DescriptionSection />
<div>
{projects.map(({ node: project }) => (
<AppSection app={project} />
))}
</div>
<FooterSection />
</Layout>
);
}
//export page query
export const query = graphql`
query Apps {
apps: allAppsJson(sort: { order: ASC, fields: order }) {
edges {
node {
appLink
title
tagline
moreLink
order
icon
}
}
}
}
`;
Here is the component.
export default function App(props) {
const query = useStaticQuery(graphql`
query AppSectionImages {
icon: file(relativePath: { eq: "EzMaxRequest/AppIcon_180.png" }) {
childImageSharp {
gatsbyImageData(
width: 200
placeholder: BLURRED
formats: [AUTO, WEBP, AVIF]
)
}
}
}
`);
const image = getImage(query.icon);
const app = props.app;
return (
<div>
<h1>{app.title}</h1>
<GatsbyImage image={image} alt={app.title} />
</div>
);
}
You have a few options:
Query for all of your image data in your page query and prop-drill the data to the component that uses it to display the image.
Using Gatsby v3+, hardcode the image references for each component using the new StaticImage component.
If you have a single component used multiple times with different content/images, but a static parent component with your content, you can leverage option #2 above but pass the image component down as a prop or children.

Markdown in Antd

I can confirm that this has neither been asked nor been addressed anywhere. I am currently working on a website using Gatsby, Strapi and Antd for the design. I am using the the rich text editor for one of the content types where I have put all my markdown content. However, when I try to display the actual content on the webpage, the styling is completly nuked. I figured this was because the content uses normal HTML elements like <h1> and <p> instead of the antd components like <Title> or <Text>.
So I did some researches and found that Antd has a markdown.less in their source, which I figure is used to style the markdown in their documentation. I haven't found the same after scouring the source code inside the node modules folder. Does this mean that Antd does not support styling for markdown or am I missing something here?
Btw I am using the react-markdown library to display the all the markdown. I have also posted all the relevant code below.
template.tsx
const ProductTemplate: React.FC<Props> = ({ data }: Props) => {
const {
...
} = data
const {
product: { strapiId: selectedKeyProp },
} = data
return (
<Layout>
<AntLayout>
<ProductSidebar
selectedKeyProp={selectedKeyProp}
productsInfo={productsInfo}
>
<ProductInfo product={product} />
</ProductSidebar>
</AntLayout>
</Layout>
)
}
export const query = graphql`
...
`
export default ProductTemplate
page-component.tsx
const ComponentName = ({ data }) => {
const {
...
} = data
console.log(data)
return (
<Layout>
<AntLayout>
<ProductSidebar productsInfo={productsInfo}>
<div style={{ display: "unset", padding: "15px 35px" }}>
<ReactMarkdown className="markdown" children={content} />
</div>
</ProductSidebar>
</AntLayout>
</Layout>
)
}
export const query = graphql`
...
`
export default ComponentName
There problem can be pointed out here I guess. The <ReactMarkdown> receives the markdown content in the children props. But once the content is displayed to the page, the styling, as I mentioned above, is nuked out.
I raised this issue on github here. According to the devs, there is currently no markdown support for antd. As I thought, the markdown.less file linked above was just for the markdown in their documentation.
One way to get around this solution is to include the tags exactly as specified by antd component elements. For example, instead of a # or <h1> for a header, we can use <h1 class="antd typography">, though this definitely is painful and prone to error.
This other solution would be to use MDX, which support jsx inside markdown.
About a month late, but I just ran into this: I needed a way to change the styling of html generated by the react-markdown React component. I am using react-markdown in a NextJS app, and using Antd as my component library. I am additionally using react-syntax-highlighter and react-syntax-highlighter-prismjs for handling lighting codeblocks
While there is no support in Antd for markdown, there is support for custom components in react-markdown! react-markdown allows you to override the rendering engine and replace the individual components with your own, so I went through and replaced a bunch of them with Antd components:
Fair warning: This was my first pass to make sure this worked, it doesn't for instance handle checkboxes inside list item inputs.
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import {coy} from "react-syntax-highlighter/dist/cjs/styles/prism/prism";
import gfm from 'remark-gfm';
import { Typography } from 'antd';
const { Title, Text } = Typography;
import { List } from 'antd';
const mymarkdowndata = '
# A heading
Some text
## A second heading
* List!
* Has
* Many
* Items!
'
const renderers = {
heading: function Heading(props) {
return <Title level={props.level}>{props.children}</Title>;
},
list: function MakeList(props) {
return <List bordered>{props.children}</List>
},
listItem: function MakeListItem(props) {
return <List.Item>{props.children}</List.Item>
},
inlineCode: function makeInlineCode(props) {
return <Text code>{props.children}</Text>
},
code: function makeCodeBlock(props) {
return <SyntaxHighlighter language={props.language} style={coy}>{props.value}</SyntaxHighlighter>
},
blockquote: function makeBlockQuote(props) {
return <Text type="secondary">{props.children}</Text>
}
};
Then inside your component rendering function:
render() {
return <ReactMarkdown renderers={renderers} plugins={[gfm]} children={mymarkdowndata} />
}

Image preview using Dropzone extending File type

Would like to preview an image before uploading using dropzone. I am trying to map the images by calling file.preview but it does not exist on type File. Dropzone extends the file type with a preview?: string. My question is how do I access the extended type? How do I show an image with the URL.createObjectURL I'm using. I am new to TypeScript and cannot wrap my head around the problem. Any help is appreciated.
I have tried creating a custom interface, importing it and assigning it to files: Pic[] to no success. Complains that the properties do not exist on the accepted files. I understand that. Perhaps I have to declare what is what in the function? No idea.
export interface Pic {
picId: string
name: string
preview: string
}
I have Object.assigned a preview URL within the onDrop function. It carries over but still cannot access it through files.preview.
I tried using a constructor to get the extended type but get completely lost in what I'm trying to achieve.
import * as React from "react"
import styles from "../../styles.scss"
import Dropzone from "react-dropzone"
class ImageUpload extends React.Component {
state = {
files: []
}
handleOnDrop = (files: File[]) => {
files.map(file => Object.assign(file, {
preview: URL.createObjectURL(file)
}))
this.setState({ files: [...this.state.files, ...files] })
console.log(files)
}
render() {
const files = this.state.files.map((file: File) => (
console.log(file),
(
<li key={file.name}>
{file.preview}
</li>
)
))
return (
<form>
<div>
<h2>Upload images</h2>
</div>
<Dropzone onDrop={this.handleOnDrop} accept="image/*" />
<ul>{files}</ul>
</form>
)
}
}
export default ImageUpload
Expected results: file.preview URL works and I can use it as a background image.
Actual results: file.preview does not exist on type File.
My question is how do I access the extended type?
By importing the interface from the type definitions:
import Dropzone, { FileWithPreview } from "react-dropzone"
Aside of that question. The Array.map function returns a new Array. Currently you're only mapping the files array, but you're not doing anything with the new Array. You would want to use that new array to set your state with:
const filesWithPreviews: FileWithPreview[] = files.map(
// Add the preview property to each file element
);
this.setState({ files: [...this.state.files, ...filesWithPreviews] });
And seeing as you're new to TypeScript. You should set an interface to match your state.
interface IState {
files: FileWithPreview[];
}
class ImageUpload extends React.Component<{}, IState> {
}
This way TypeScript lets you know that you're trying to store an incorrect type of an element to the state which you'd like (like that is happening right now in your dropHandle function). And you wouldn't have to explicitly say that a file is a File in your render function, when mapping the array.
Sadly, #Alserda's answer is no longer fully applicable because the FileWithPreview export appears to have been dropped from React Dropzone after version 6 (circa 2018).
I ended up using the suggestion in #Prozak's reply to that answer, which was to recreate the type (actually an interface) in my own code, like so:
export interface FileWithPreview extends File {
preview?: string;
}
(This is exactly how it was defined in React Dropzone itself, pre-version 7.)
Once you have the interface, you can plug it into your own code as shown in #Alserda's answer.

Simple variable placeholders in Draft.js

We're trying to implement variable placeholders for a feature in our app.
Essentially the user will create a template using a WYSIWYG editor and put variables into the text e.g:
Hello {{first_name}}
These variables will then be interpolated by our back end (e.g. swapping {{first_name}} with "Peter").
Our users are non-technical so we don't want them to have to add the placeholders manually. We want them to drag and drop the variables into their text from a list that we predefine.
Is there a simple way of doing this with DraftJS or CKEdior out of the box?
Thanks for A2A.
We had a similar feature requirement to add templates with placeholders for mailing.
Dear {{recipient.first_name}},
Mail Body Template
With best regards,
{{sender.first_name}} {{sender.last_name}}
Manager - ABC Company.
Here is how we implemented it. I hope this would be helpful.
draft-js provides a Modifier class for this very use-case.
Modifier API has insertText method to insert custom placeholder text in the content-{{first_name}} in your case.
import { EditorState, Modifier } from 'draft-js';
insertPlaceholder = placeholderText => {
const { editorState } = this.state;
const newContentState = Modifier.insertText(
editorState.getCurrentContent(), // get ContentState from EditorState
editorState.getSelection(),
placeholderText
);
this.setState({
editorState: EditorState.createWithContent(newContentState); // get EditorState with ContentState
});
}
Then add a button to trigger the insertPlaceholder method.
<button
onClick={() => this.insertPlaceholder('{{first_name}}')}
>
Add First Name
</button>
When clicked on Add First Name placeholder text will be inserted at the current cursor position.
If the editor is out of focus, the text will be inserted at the beginning.
For Reusability, you can create a custom plugin and include it in options.
Placeholder plugin example screenshot
Note:
If you have multiple placeholders, I would rather suggest using a select input with possible placeholder options - this will keep UI clean instead of having a bunch of buttons.
Any suggestions are welcome.
You can create a button as follows
<button
onMouseDown={e => this.onAddText(e, '{{first_name}}' )}
>
ADD Name
</button>
and inside onAddText function
import { insertText } from 'draft-js-modifiers';
onAddText = (e, text) => {
e.preventDefault();
const { editorState } = this.state;
const EditorStat = insertText(editorState, text);
this.setState({ editorState: EditorStat });
};
insertText is method provided by draft-js-modifiers
and then use this.state.editorState as follows:
import {
Editor,
} from 'draft-js';
<Editor
editorState={this.state.editorState}
/>

ESLint React PropTypes with JSX

I'm using the Airbnb javascript standards to lint my react app and i'm getting an error i'm not sure how to fix. I'm passing in another component in order to create a page about the component itself, including the component itself (children) a title and a markdown description. I get a bunch of errors for each of those items like this.
'children' is missing in props validation - react/prop-types
I can see that it is because i'm not defining props, but I can't figure out how to get it working.
I'm essentially outputting something like the below.
const example = ({ title, children, description }) => (
{title}
{md.render(description)}
{children}
)
How would I define my Props in this situation?
I ended up working this out, I just had typos in my original code! 😅
const example = ({ title, children, description }) => (
{title}
)
example.propTypes = {
title: PropTypes.string,
}

Resources