In this code, I am trying to insert a code block using react-quilljs
import React, { useState } from 'react';
import hljs from 'highlight.js';
import { useQuill } from 'react-quilljs';
import 'quill/dist/quill.snow.css'; // Add css for snow theme
export default () => {
hljs.configure({
languages: ['javascript', 'ruby', 'python', 'rust'],
});
const theme = 'snow';
const modules = {
toolbar: [['code-block']],
syntax: {
highlight: (text) => hljs.highlightAuto(text).value,
},
};
const placeholder = 'Compose an epic...';
const formats = ['code-block'];
const { quill, quillRef } = useQuill({
theme,
modules,
formats,
placeholder,
});
const [content, setContent] = useState('');
React.useEffect(() => {
if (quill) {
quill.on('text-change', () => {
setContent(quill.root.innerHTML);
});
}
}, [quill]);
const submitHandler = (e) => {};
return (
<div style={{ width: 500, height: 300 }}>
<div ref={quillRef} />
<form onSubmit={submitHandler}>
<button type='submit'>Submit</button>
</form>
{quill && (
<div
className='ql-editor'
dangerouslySetInnerHTML={{ __html: content }}
/>
)}
</div>
);
};
Using the above code, I get the following preview of the editor's content
There are two problems with this:
There is no code syntax highlighting, as I want to achieve this using the highlihgt.js package, inside the code block inside the editor, and
The code block is not displayed (with the black background and highlighting syntax when it's working) in the previewing div outside the editor.
How can I fix these two issues?
Your code is getting marked up by highlight.js with CSS classes:
<span class="hljs-keyword">const</span>
You are not seeing the impact of those CSS classes because you don't have a stylesheet loaded to handle them. You need to choose the theme that you want from the available styles and import the corresponding stylesheet.
import 'highlight.js/styles/darcula.css';
Look at the css in the editor mode. It depends on two class names ql-snow and ql-editor.
You can fix this issue by wrapping it around one more div with className ql-snow.
<div className='ql-snow'>
<div className='ql-editor' dangerouslySetInnerHTML={{ __html: content }}>
<div/>
</div>
This should work.
I got the same issue and when I used hjls what happened was that I got syntax highlighting in the editor but not in the value.
If you noticed the syntax gets highlighted some seconds after you write the code block, this means that the value gets set before the syntax gets highlighted.
So, I just set the value after 2 seconds using setTimeout and this solved my problem
Like this:
<ReactQuill
theme="snow"
value={value}
onChange={(content) => {
setTimeout(() => {
setValue(content)
}, 2000)
}}
modules={modules}
formats={formats}
bounds="#editor"
placeholder="Write something..."
className="text-black dark:text-white"
/>
I recently implemented this logic into my project. I used React Quill for the text editor, implemented syntax highlighting to it using highlight.js, and I also used React Markdown to display the formatted content on my website. React Markdown by default works with markdown, so you need a plugin (rehype-raw) to get it to parse HTML. This is my code, from my project. Just remove some of the unnecessary stuff from here that is specific to my project and use what you need.
// PLUGINS IMPORTS //
import { Typography } from "#mui/material";
import { useEffect, useState } from "react";
import hljs from "highlight.js";
import "react-quill/dist/quill.core.css";
import "react-quill/dist/quill.snow.css";
import "highlight.js/styles/atom-one-dark.css";
import ReactQuill from "react-quill";
import ReactMarkdown from "react-markdown";
import rehypeRaw from "rehype-raw";
// COMPONENTS IMPORTS //
import { CreateButton } from "components/atoms";
// EXTRA IMPORTS //
import styles from "./create-post.module.css";
import { ETheme } from "types/theme";
/////////////////////////////////////////////////////////////////////////////
type CreatePostProps = {};
hljs.configure({
// optionally configure hljs
languages: ["javascript", "python", "c", "c++", "java", "HTML", "css", "matlab"],
});
const toolbarOptions = [
["bold", "italic", "underline", "strike"],
["blockquote", "code-block"],
[{ list: "ordered" }, { list: "bullet" }],
["link"],
[{ indent: "-1" }, { indent: "+1" }],
[{ header: [1, 2, 3, 4, 5, 6, false] }],
[{ align: [] }],
];
const modules = {
syntax: {
highlight: function (text: string) {
return hljs.highlightAuto(text).value;
},
},
toolbar: toolbarOptions,
clipboard: {
// toggle to add extra line breaks when pasting HTML:
matchVisual: false,
},
};
const formats = [
"header",
"font",
"size",
"bold",
"italic",
"underline",
"strike",
"blockquote",
"code-block",
"list",
"bullet",
"indent",
"link",
"align",
];
const placeholder = "Description";
const CreatePost = (props: CreatePostProps) => {
const [markdownText, setMarkdownText] = useState("");
useEffect(() => {
console.log({ markdownText });
}, [markdownText]);
return (
<main className={`${styles["create-post-wrapper"]}`}>
<header className={`${styles["create-post-header"]}`}>
<Typography variant="h6">Create a post</Typography>
</header>
{/* making the border a seperate div makes it easier to apply margin */}
<div className={`${styles["border"]} ${styles["top"]}`}></div>
<div>Choose a community</div>
<article className={`${styles["create-post"]}`}>
<section className={`${styles["inner-create-post"]}`}>
<section>Title</section>
<ReactQuill
value={markdownText}
onChange={value => setMarkdownText(value)}
theme="snow"
modules={modules}
formats={formats}
placeholder={placeholder}
/>
<div className="ql-snow">
<div className="ql-editor">
<ReactMarkdown children={markdownText} rehypePlugins={[rehypeRaw]} />
</div>
</div>
<div className={`${styles["border"]} ${styles["bottom"]}`}></div>
<section className={`${styles["post-button"]}`}>
<CreateButton
theme={ETheme.LIGHT}
buttonText="Post"
buttonProps={{
fullWidth: false,
}}
/>
</section>
</section>
</article>
</main>
);
};
export default CreatePost;
You can always add more options to toolbarOptions, but don't forget to also add them to formats if you do. Also, if you want to keep formatting anywhere else in your website, you need the two divs with these 2 classes around your markdown.
React Quill bacially saves everything into a string with HTML and classes, you import styles for those classes and it works like magic.
Related
I'm using Stitches in React to handle my CSS and theming. I have the following code:
import React from 'react'
import { createStitches, globalCss } from '#stitches/react'
const { theme, createTheme } = createStitches({
theme: {
colors: {
text: 'blue',
bodyBg: 'lightgray',
},
},
})
const darkTheme = createTheme('dark-theme', {
colors: {
bodyBg: 'black',
},
})
const globalStyles = globalCss({
'body': {
color: '$text',
backgroundColor: '$bodyBg'
},
});
function App() {
globalStyles()
return (
<div className='App'>
<h1>Hello World!</h1>
</div>
)
}
export default App
As you can see, I have a default theme, and then a dark theme that extends the default theme while overriding some properties (in this case, the bodyBg). I'm applying these styles directly in my <body>. The default theme works fine, but the dark theme does not. When I add the .dark-theme class to my <html>, nothing changes (the background should turn black). What exactly am I doing wrong here?
You are probably trying to add the class directly to the body in the developer tools which doesn't work.
I managed to make it work with a button onClick event:
const darkTheme = createTheme('dark-theme', {
colors: {
bodyBg: 'black',
},
})
function App() {
const setBlackTheme = () => {
document.body.classList.remove(...document.body.classList);
// Here we set the darkTheme as a class to the body tag
document.body.classList.add(darkTheme);
}
globalStyles()
return (
<div className='App'>
<button onClick={setBlackTheme}>Dark Theme</button>
<h1>Hello World!</h1>
</div>
)
}
export default App
Try it and let's see if it works for you as well.
I want to run this component inside react native
<StyledFirebaseAuth uiConfig={uiConfig} firebaseAuth={firebase.auth()} />
currently, I am using react-native-render-html package
and doing likewise
import React from "react";
import { View, Text } from "react-native";
import HTML from "react-native-render-html";
import StyledFirebaseAuth from "react-firebaseui/StyledFirebaseAuth";
const PhoneAuth = () => {
const uiConfig = {
signInFlow: "popup",
signInOptions: [
{
provider: firebase.auth.PhoneAuthProvider.PROVIDER_ID,
recaptchaParameters: {
type: "image",
size: "invisible",
badge: "bottomleft",
},
defaultCountry: "+91",
whitelistedCountries: ["IN", "+91"],
},
],
// callbacks: {
// signInSuccessWithAuthResult: function (authResult) {
// var user = authResult.user;
// const data = { phone: user.phoneNumber };
// props.setPhoneNumber(data);
// },
// },
};
const htmlContent =
`<StyledFirebaseAuth uiConfig={uiConfig} firebaseAuth={firebase.auth()} />`
return (
<View>
<HTML html={htmlContent} />
</View>
);
};
export default PhoneAuth;
but as HTML content is a string it's not picking the variables
and I get a blank screen.
If you're creating a custom HTML tag or element you have to tell the renderers to do so, i.e.:
const content = `<bluecircle></bluecircle>`;
...
renderers: {
bluecircle: () => <View style={{ width: 20, height: 20, borderRadius: 10, backgroundColor: 'blue' }} />
}
You might try the following
import HTML from 'react-native-render-html'
...
render() {
// The html you want to render
const html = `
<div>
</div>
`
const styles = {}
const renderers = {
StyledFirebaseAuth: (htmlAttribs, children, passProps) => {
return (
<StyledFirebaseAuth
{...passProps} />)
}
}
return (
<HTML
// Required. The html snippet you want to render as a string
html={html}
// The styles to supply for each html tag. Default styles
// are already pre-provided in HTMLStyles.js. The additional
// styles that you provide will be merged over these, so if
// you need some funky red background on your h1, just set
// the background
htmlStyles={styles}
// Renderers to use for rendering specific HTML elements.
// Default renderers are pre-provided in HTMLRenderers.js.
renderers={renderers}
)
}
See docs
set style to view flex: 1 as showed in example
<ScrollView style={{ flex: 1 }}>
<HTML
html={htmlContent}
imagesMaxWidth={Dimensions.get("window").width}
/>
</ScrollView>
You can use WebView for this purpose. No need to use any other library.
<WebView
originWhitelist={['*']}
source={{ html: htmlContent }}
/>
See API reference for more details.
I was trying to test my component style with Jest and Enzyme. I'm using TailwindCSS as my CSS framework and non ejected create-react-app. So here is my component:
import React from 'react'
import './tailwind.css' // the tailwind css
export function Alert({ children, className, icon, ...resProps }) {
return (
<div data-testid="qa-alert" className="my-2 p-3 text-lg bg-red-200 rounded-md flex items-center justify-center} {...resProps}>
{icon && React.createElement(icon, { className: 'mr-2 text-xl' })}
{children}
</div>
);
}
I want to test that the background color is red (bg-red-200 or #fed7d7) by using the getComputedStyle method, here is my test:
it('Should have red background color', () => {
const wrapper = mount(<Alert />);
const alert = wrapper.find('div');
const style = getComputedStyle(alert.getDOMNode());
console.log(style);
expect(style.backgroundColor).toBe('#fed7d7');
});
but the test fails because I don't get the background style. The received value is empty string. and the log of style is
CSSStyleDeclaration {
'0': 'display',
_values: { display: 'block' },
_importants: { display: '' },
_length: 1,
_onChange: [Function]
}
how can i get the style?
You can try to use jest-dom method toHaveStyle():
expect(alert).toHaveStyle({backgroundColor: '#fed7d7'});
You can archive it by update:
The selector wrapper.find('div') is too generic. Use wrapper.find('div[data-testid="qa-alert"]') or wrapper.find({'data-testid': "qa-alert"]})
use Enzyme hasClass to test
Example:
it('Should have red background color', () => {
const wrapper = mount(<Alert />);
const alert = wrapper.find({'data-testid': "qa-alert"]});
expect(alert.hasClass('bg-red-200')).toEqual(true);
});
I am just trying a React Select Dropdown Example using the below code:
import React from 'react';
import Select from 'react-select';
import '../../../node_modules/bootstrap/dist/css/bootstrap.min.css';
const techCompanies = [
{ label: "Apple", value: 1 },
{ label: "Facebook", value: 2 },
{ label: "Netflix", value: 3 },
{ label: "Tesla", value: 4 },
{ label: "Amazon", value: 5 },
{ label: "Alphabet", value: 6 },
]
const App = () => {
return (
<div className="container">
<div className="row">
<div className="col-md-4" />
<div className="col-md-4">
<Select options={ techCompanies } />
</div>
<div className="col-md-4" />
</div>
</div>
)
};
export default App
Before this , I installed react select
npm i react-select
also installed bootstrap
npm install bootstrap --save
But after running I am getting the error:
Unable to resolve "../../../node_modules/bootstrap/dist/css/bootstrap.min.css" from "components/university/App.js"
Failed building JavaScript bundle.
I can see the bootstrap.min.css under node_modules folder.
If I comment the import I am get the following :
View config not found for name div. Make sure to start component names with a capital letter.
Can anyone tell where am I going wrong?Thanks in Advance.
You can't use html components or the usual web css in react-native. In contrast to react web, react-native will map your components to native android, ios or windows ui components. Therefore your components must be composited of react-native components. I think you should check out this answer which explains the difference between react and react-native in more depth.
In your case you could start with an App Component similiar to this:
import React, { useState } from "react";
import { View, Picker, StyleSheet } from "react-native";
const techCompanies = [
{ label: "Apple", value: 1 },
{ label: "Facebook", value: 2 },
{ label: "Netflix", value: 3 },
{ label: "Tesla", value: 4 },
{ label: "Amazon", value: 5 },
{ label: "Alphabet", value: 6 },
]
const App = () => {
const [selectedValue, setSelectedValue] = useState(1);
return (
<View style={styles.container}>
<Picker
selectedValue={selectedValue}
style={{ height: 50, width: 150 }}
onValueChange={itemValue => setSelectedValue(itemValue)}
>
{techCompanies.map(({ label, value }) =>
<Picker.Item label={label} value={value} key={value} />)}
</Picker>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 40,
alignItems: "center"
}
});
export default App;
see https://reactnative.dev/docs/picker for reference.
Old answer, before we learned that this is about react-native:
Your original code actually looked fine and should work.
However the css import should usually be your first import, because you want styles inside imported components to take precedence over the default styles. source
The added braces and return are not needed at all...
Also I would recommend using
import 'bootstrap/dist/css/bootstrap.min.css'
instead of
import '../../../node_modules/bootstrap/dist/css/bootstrap.min.css'. The Babel+Webpack setup of create-react-app will take care of figuring out the location of the bootstrap module. If you use a relative path instead, there will be errors as soon as you move the file with the import...
Maybe something went wrong during your package installation? I'd suggest that you just try this:
rm -rf ./node_modules
npm install
npm run start
You can verify that your code was working in this sandbox: https://codesandbox.io/s/react-select-bootstrap-sample-g2264?file=/src/App.js
Your import looks fine. Sometimes the error messages throw us off track, as the real error is caused by some other compiling issue. So, try fixing this first:
You are confusing parentheses and curly braces. Plus, you are missing a return. Should be:
const App = () => {
return (
<div className="container">
<div className="row">
<div className="col-md-4" />
<div className="col-md-4">
<Select options={ techCompanies } />
</div>
<div className="col-md-4" />
</div>
</div>
);
};
I have an app using Material UI Beta where I try to style a simple component as follows:
import { MuiThemeProvider } from 'material-ui/styles';
const styles = theme => ({
container: {
display: 'flex',
flexWrap: 'wrap',
},
textField: {
marginLeft: 200,
marginRight: theme.spacing.unit,
width: 200,
},
menu: {
width: 200,
},
});
export const CreateJob = (props) => {
const { classes } = props;
let confirmDelete = () => {
const r = window.confirm("Confirm deletion of job");
return r === true;
};
return (
<MuiThemeProvider>
<div>
<form onSubmit={props.isEditting ? props.handleEdit : props.handleSubmit} noValidate autoComplete="off">
<h2>Update job details</h2>
<TextField
error={props.jobIdError !== ''}
helperText={props.jobIdError || "Example: ES10"}
autoFocus
margin="dense"
id="jobId"
label="Job ID"
name="jobid"
fullWidth
onChange={props.handleInputChange('jobId')}
value={props.jobId} />
</form>
</div>
</MultiThemeProvider>
I then use this in my parent component as follows:
<CreateJob open={this.state.open} />
However, this yields the following error:
TypeError: Cannot read property 'classes' of undefined
this.state is not defined in your code. In the example, state is defined as
state = {
name: 'Cat in the Hat',
age: '',
multiline: 'Controlled',
currency: 'EUR',
};
Sorry I'm kinda late with an answer, but I just found this question while searching for another solution.
I'm going to assume you also imported withStyles.
Firstly, you don't need to export both the simple component and the enhanced one:
export const CreateJob = props => {...} // lose the 'export'
export default withStyles(styles)(CreateJob); // only export here
Secondly, a real problem: <MuiThemeProvider> should be placed around your highest component(usually the <App> component that you render in your entry point file), so you can customize the default theme to your liking for the whole app; see their example here. I'm not sure, but this might even solve your problem, since that should have thrown another error like in this issue.
I just hope this helps someone, but I cannot be sure about what your exact problem is without the complete component file.