React Draft.js toolbar plugin not showing - reactjs

I've followed the docs on installing the inline and static toolbar plugins, but they seems to be nonexistent.
I'm using the Create React App CLI.
The component:
import React from 'react';
import {EditorState} from 'draft-js';
import Editor from 'draft-js-plugins-editor';
import createInlineToolbarPlugin from 'draft-js-inline-toolbar-plugin';
import createToolbarPlugin from 'draft-js-static-toolbar-plugin';
import 'draft-js/dist/Draft.css';
import 'draft-js-inline-toolbar-plugin/lib/plugin.css';
import 'draft-js-static-toolbar-plugin/lib/plugin.css';
const inlineToolbarPlugin = createInlineToolbarPlugin({
//I read somewhere that this plug-in needs this structure passed to it,
//but the example in the docs did not use it, and they are undefined anyway
// structure: [
// BoldButton,
// ItalicButton,
// UnderlineButton,
// CodeButton,
// Separator,
// ],
});
const toolbarPlugin = createToolbarPlugin();
class TextEditor extends React.Component {
constructor(props) {
super(props);
this.state = {editorState: EditorState.createEmpty()};
this.onChange = (editorState) => this.setState({editorState});
}
render() {
return (
<Editor
editorState={this.state.editorState}
onChange={this.onChange}
plugins={[inlineToolbarPlugin, toolbarPlugin]}
/>
);
}
}
export default TextEditor;
That component is then passed to another component that just renders the editor and does nothing else.
I must be missing something, or not giving the plugins what they need, I just don't know what. I'm guessing the code I have is insufficient to start adding plugins in the first place?

You can define custom buttons to perform the desired operations like below:
<Editor
editorState={this.state.editorState}
onChange={this.onChange}
plugins={[inlineToolbarPlugin, toolbarPlugin]}
/>
<button onClick={this._onBoldClick.bind(this)}>Bold</button> //add button to make bold
And now you can write a code to make bold in _onBoldClick method as follows:
_onBoldClick() {
this.onChange(RichUtils.toggleInlineStyle(this.state.editorState, 'BOLD'));
}
You can take reference from the docs.

You need to import the buttons before you can create the toolbar
import {
ItalicButton,
BoldButton,
UnderlineButton,
CodeButton
} from "draft-js-buttons";
Also, you need to include the toolbar in your render function
const { Toolbar } = inlineToolbarPlugin;
render() {
return (
<div>
<Editor
editorState={this.state.editorState}
onChange={this.onChange}
plugins={[inlineToolbarPlugin, toolbarPlugin]}
/>
<Toolbar />
</div>
);

Related

Is this only possible with external URLs and not local?

I'm trying to make a photo gallery using react-images, the URLs are correct but the photos themselves are not loading into my web app. I get the broken image icon when switching themodalIsOpen:false to true.
Ive tried looking up examples of the same problems and alternatives, like if the component was configured right or if I am extending it right in the class.
import React, { Component } from 'react';
import Carousel, { Modal, ModalGateway } from 'react-images';
import blksmith from '../images/gallery/illustration/Blacksmith.jpg';
import mage from '../images/gallery/illustration/Mage.jpg';
const images =
[
{
src:{blksmith}
} ,
{
src:{mage}
}
];
class illuGallery extends Component {
state = { modalIsOpen: false }
toggleModal = () => {
this.setState(state => ({ modalIsOpen: !state.modalIsOpen }));
}
render() {
const { modalIsOpen } = this.state;
return (
<ModalGateway>
{modalIsOpen ? (
<Modal onClose={this.toggleModal}>
<Carousel
views={images}
/>
</Modal>
) : null}
</ModalGateway>
);
}
}
export default illuGallery;
This is in the actual gallery.js file, the web page that renders the gallery.
import React from 'react';
import Layout from "../components/layout";
import IlluPhotos from "../components/illustrationGallery";
import SEO from "../components/seo";
import './gallery.scss';
const GalleryPage = () => {
return (
<Layout>
<div style={{width:'100%',height:'250px'}}>
<SEO title="Gallery" />
<IlluPhotos/>
</div>
</Layout>
)
}
export default GalleryPage;
I am seeking some feedback on how to get this to work and what I did wrong, or what I should explore more.
So I ended up adding the pictures I wanted for the gallery to the public folder as mentioned farther down in this post
Since the https://localhost:8000 was appearing in front of the links to the images I wanted to use.
Thank you all for helping me find the answer!!
You don't need to import images.
According to react-images documentation, you just need to pass path to image as a string to <Carousel> component, like in this example below:
import React from 'react';
import Carousel from 'react-images';
const images = [{ src: 'path/to/image-1.jpg' }, { src: 'path/to/image-2.jpg' }];
class Component extends React.Component {
render() {
return <Carousel views={images} />;
}
}

How to make an API request In React on button click

I'm trying to build a random quote generator that loads A quote on componentDidMount with an axios api request , then loads new quotes on button click.
This is for A freecodecamp project. I have tried making the call again on button click, then adding the new response to state, but it will not work at all.
import React, { Component } from 'react'
import Button from './Button';
import axios from 'axios'
class QuoteBox extends Component{
constructor(props){
super(props)
this.state = {
quotes: []
}
}
componentDidMount(){
axios.get('http://quotesondesign.com/wp-json/posts?
filter[orderby]=rand&filter[posts_per_page]=1')
.then(res=> this.setState({quotes: res.data[0]}))
}
getNext = (ev) =>{
ev.preventDefault()
axios.get('http://quotesondesign.com/wp-json/posts?
filter[orderby]=rand&filter[posts_per_page]=2')
.then(res=> this.setState({quotes:[...this.state,res.data[0]]}))
}
render(){
const {content,title} = this.state.quotes
const filteredContent = String(content).replace(/(<\w>)|(<\/\w>)|
(&#\d{4})/gm, "").replace(/(;)/g,"'")
console.log(content)
return(
<React.Fragment>
<h2>A little inspiration for the day</h2>
<div className='outerQuoteBox'>
<div className='innerQuoteBox'>
<p>{filteredContent}</p><br/><br/>{title}
</div>
<Button getNext={this.getNext} />
</div>
</React.Fragment>)
}
}
export default QuoteBox
And this is my button component
import React, { Component } from 'react'
export class Button extends Component {
render() {
return (
<React.Fragment>
<button onClick={this.props.getNext} className='nextBtn'
type='button'>Next</button>
</React.Fragment>
)
}
}
export default Button
When I click the button, it seems like the request isn't going through at all. If i check State in the dev tools, only the first quote from componentDidMount is in the array. I don't understand where my mistake is.
Edit: I had used the wrong prop reference, so it wasn't making the call. I fixed this and it does make the call now, and it brings in one new quote, but that's it. And it doesn't add the new one to state, it just replaces it with the one new one. and that's all it will do. The api instructions say the end point i'm using should return a new random quote, but it does not.
It looks like you're referencing the wrong prop on the button.
Change getQuote to getNext and it should work...
import React, { Component } from 'react'
export class Button extends Component {
render() {
return (
<React.Fragment>
<button onClick={this.props.getNext} className='nextBtn'
type='button'>Next</button>
</React.Fragment>
)
}
}
export default Button

autocomplete is undefined with material-ui

I have the following code in a jsx file and I get error:
Uncaught ReferenceError: AutoComplete is not defined
From what I see it should be working ok, Code:
import React, {Component} from 'react';
import { Autocomplete } from 'material-ui';
class MaterialUIAutocomplete extends Component {
constructor(props) {
super(props);
this.onUpdateInput = this.onUpdateInput.bind(this);
this.state = {
dataSource : [],
inputValue : ''
}
}
onUpdateInput(inputValue) {
}
render() {
return <AutoComplete
dataSource = {this.state.dataSource}
onUpdateInput = {this.onUpdateInput} />
}
}
export default MaterialUIAutocomplete;
It's a typo, you are importing Autocomplete and using AutoComplete.
Use these ways to import AutoComplete:
import { AutoComplete } from 'material-ui';
Or
import AutoComplete from 'material-ui/AutoComplete';
Update:
To render material-ui component we need to add the default theme and styling, include these lines in your component, like this:
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
const muiTheme = getMuiTheme({});
Then render the AutoComplete inside MuiThemeProvider:
render() {
return <MuiThemeProvider muiTheme={muiTheme}>
<AutoComplete
dataSource = {this.state.dataSource}
onUpdateInput = {this.onUpdateInput} />
</MuiThemeProvider>
}
Use this:
import React, {Component} from 'react';
import AutoComplete from 'material-ui/AutoComplete';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
const muiTheme = getMuiTheme({});
class MaterialUIAutocomplete extends Component {
constructor(props) {
super(props);
this.state = {
dataSource : [],
inputValue : ''
}
this.onUpdateInput = this.onUpdateInput.bind(this);
}
onUpdateInput(inputValue) {
}
render() {
return <MuiThemeProvider muiTheme={muiTheme}>
<AutoComplete
dataSource = {this.state.dataSource}
onUpdateInput = {this.onUpdateInput} />
</MuiThemeProvider>
}
}
export default MaterialUIAutocomplete;
Note: MuiThemeProvider is not required to include inside each component, you can use this in main page and then you can use any material-ui component inside any component.
this looks like a migration issue to me - you want to use the material-ui 0.x AutoComplete, but you have installed the new material-ui v1.x.
as such, you need to follow the Migration steps and in order to use any v0.x component, put this wherever you create/declare your themes:
<MuiThemeProvider theme={theme}>
<V0MuiThemeProvider muiTheme={themeV0}>
{/*Components*/}
</V0MuiThemeProvider>
Because the new 1.5 theme is available via props, the old one through context, you need to include both for AutoComplete to have reference to the old theme. I wouldn't do this unless you really need something from the old library, such as the AutoComplete widget.
https://material-ui.com/guides/migration-v0x/

React and setState and autocomplete

Im using react and material ui.
This is my component
```
import React from 'react';
import lightBaseTheme from 'material-ui/styles/baseThemes/lightBaseTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import AutoComplete from 'material-ui/AutoComplete';
// the light theme
const lightMuiTheme = getMuiTheme(lightBaseTheme);
// the autocomplete component width
const Preferencestypeahead = {
maxWidth: 600,
position:'relative',
margin:'auto'
}
export default class Preferences extends React.Component {
constructor(props) {
super(props);
this.handleUpdateInput = this.handleUpdateInput.bind();
this.state = {
dataSource: [],
};
}
handleUpdateInput(value){
this.setState({
dataSource: [
value,
value + value,
value + value + value,
],
});
};
render() {
return (
<div>
<MuiThemeProvider muiTheme={lightMuiTheme}>
<section style={Preferencestypeahead}>
<AutoComplete
hintText="Type"
dataSource={this.state.dataSource}
onUpdateInput={this.handleUpdateInput.bind(this)}
floatingLabelText="Search"
fullWidth={true}
/>
</section>
</MuiThemeProvider>
</div>
)
}
}
I keep getting setState is not defined when I type anything inside the autocomplete. Where could I be going wrong? I have also faced this problem when I tried to import the tabs as well
You need to bind to "this"
This line is the problem:
this.handleUpdateInput = this.handleUpdateInput.bind();
Change it to:
this.handleUpdateInput = this.handleUpdateInput.bind(this);
Since you are already using ES6 you could just do
...
onUpdateInput={(val) => this.handleUpdateInput(val)}
...
then you don't need to do this --> this.handleUpdateInput = this.handleUpdateInput.bind(this); in your constructor

How to retrieve a string in ReactIntl 2.0 without using FormattedMessage

Please correct me if I am wrong, FormattedMessage in ReactIntl returns a string wrapped by span tags. In ReactIntl 1.2, we have the option to use this.getIntlMessage('key') to get only the string part.
Here is my question: Is there an equivalent of that in ReactIntl 2.0? I am aware that the string can be obtained by using the Function-As-Child pattern in FormattedMessage as
<FormattedMessage id="placeholder">
{(formattedValue)=>(
<MyComponent ref="mycomponent" placeholder={formattedValue}/>
)}
</FormattedMessage>
However, it messes up the 'ref' in my component and I can't access to the component using this.refs.mycomponent any more.
There is a better to solve placeholder problem.
<FormattedMessage ...messages.placeholderIntlText>
{
(msg) => <input type="text" placeholder = {msg} />
}
</FormattedMessage>
You can easily return string using intl object provided by react-intl.
this is how you use intl object inside react class in much more easier way.
note: Render Component (main component) should wrap with IntlProvider
class MySubComponent extends React.Component{
{/*....*/}
render(){
return(
<div>
<input type="text" placeholder = {this.context.intl.formatMessage({id:"your_id", defaultMessage: "your default message"})}
</div>
)
}
}
MySubComponent.contextTypes ={
intl:React.PropTypes.object.isRequired
}
By defining contextTypes it will enable you to use intl object which is a context type prop. See react context for more details.
Ok, there is a work around for that. I can add ReactIntl as the context in the component like this:
contextTypes: {
intl: React.PropTypes.object.isRequired,
},
Then when trying to retrieve the string of the message and use it, for example in a placeholder, I can do this.
<MyComponent ref="mycomponent" placeholder={this.context.intl.messages.placeholder}/>
If you are using a functional component, then you can use useIntl() hook to get intl object and get the message as string from below code snippet.
import {IntlProvider, useIntl} from 'react-intl';
export function MyFunctionalComponent() {
const intl = useIntl();
return (
<div>
<p>{intl.formatMessage({id: "MyKey"})}</p>
</div>
)
}
Note: You Parent component should be wrapped around </IntlProvider> provider.
This is what I end up with. I added a central help function that retrieves the plain text.
import { useIntl } from 'react-intl';
export const GetTranslateText = (textId) => {
const intl = useIntl();
const plainText = intl.formatMessage({ id: textId });
return plainText;
};
Then it works fine to use to just retrieve the text in code or component
import { GetTranslateText } from '/utils/IntlHelpers';
.
.
<input type="text" placeholder={GetTranslateText('your.text.id')} />
.
.
I solved this problem using React render props.
I created an npm package that implements it: http://g14n.info/react-intl-inject/
It is a component like this
import { injectIntl } from 'react-intl'
export default function reactIntlInject ({ children, ...props }) {
if (typeof children === 'function') {
return (
children(props)
)
} else {
return null
}
}
And you can use it to wrap the components that for example has props you want to translate, for example
import React, { Component } from 'react'
// Import the package I created, available on npm with MIT license....
import InjectIntl from 'react-intl-inject'
// ...or copy the code above in a file in your project and import it, somethong like
// import InjectIntl from './path/to/my/render/prop/component'
class MyComponent extends Component {
render () {
return (
<InjectIntl>
{({ intl }) => (
<button
type='submit'
value={intl.formatMessage({ id: 'enter.submit' })}
/>
)}
</InjectIntl>
)
}
}
export default injectIntl(reactIntlInject)

Resources