How to set slate value based on the plain string value(serialized) - reactjs

I am building a rich text editor with slate js on React and I need to parse URL from the content(for example: www.website.me should be transformed to www.website.me).
I have already implemented the function to parse URL from plain text and then wrap the content inside the correct tag.
However, the problem is that I need to display the parsed value as soon as the user is typing on the editor(on onChange event). Here is what I have :
onChange = ({ value }) => {
const { isFocused } = value.selection
if (isFocused !== this.state.isFocused) {
this.setState({ isFocused })
}
const string = this.state.html.serialize(value)
const linkifiedString = linkifyHtml(string) || ''
if (value.document !== this.state.value.document) {
const { onChange } = this.props
onChange && onChange(linkifiedString)
}
// this won't work and will call the onChange method infinitely
this.setState({ value: this.state.html.deserialize(linkifiedString) })
}
Thanks for your help.

Finally, I have found the solution, you have to create your own slate plugin or use this one: https://github.com/enzoferey/slate-instant-replace

Related

Clearing a Draft.js textarea in automated testing

In my react app I am using a component that is building on top of draft.js.
Unfortunately I can not interact with it as I would with a normal textarea when using react-testing.
I want to input text, clear that text and then input new text. With an empty draft.js editor it is easy enough:
const textarea = screen.getByRole('textbox');
const event = createEvent.paste(textarea, {
clipboardData: {
types: ['text/plain'],
getData: () => 'MyText',
},
});
fireEvent(textarea, event);
I now want to clear the input in order to paste a new input. If I just invoke a second paste event, it will paste the data before what is already in the textarea so:
const textarea = screen.getByRole('textbox');
const event = createEvent.paste(textarea, {
clipboardData: {
types: ['text/plain'],
getData: () => 'MyText',
},
});
fireEvent(textarea, event);
fireEvent(textarea, event);
will result in the content of the textbox being MyTextMyText.
I tried using the change event to clear the textbox:
const textarea = screen.getByRole('textbox');
const changeEvent = createEvent.change(textarea, {
target: {
value: ''
},
});
fireEvent(textarea, changeEvent);
resulting in: The given element does not have a value setter
So my question is: What is the correct way to clear a draft.js textarea between pasting new content into it?

react setState not updating state in one of my functions

I'm working an a react app with a few forms and I am trying to implement an edit form for input items. The function first opens the list item in a pre-populated form.
The editItem function currently looks like this:
editItem(event) {
event.preventDefault();
const target = event.target.parentNode.parentNode;
const { key } = target.dataset;
const { className } = target;
const currState = { ...this.state[className] };
const currItem = currState.list[key];
for (let i in currItem) {
if (i !== "list" && i !== "hidden") {
currState[i] = currItem[i]
}
}
this.setState({ [className]: currState });
this.hideUnhide({target: {name: className}});
}
I have confirmed with console logs that currState is correctly set with the values that I am looking for, and that I am not having an async issue. I am using this same format to set state in other functions in my app and all of the others are working properly. If I directly mutate state in the same place, I get the behavior I'm looking for (form fields populate), but nothing happens when I use setState.
Link to my github repo: here. The function in question is in App.js.
As Brian Thompson points out in his comment, it turns out that the hideUnhide function call directly after my setState uses setState as well and writes over the first setState call with the previous state:
hideUnhide(event) {
const { name } = event.target;
const currState = { ...this.state[name] };
if (currState.hidden === true) {
currState.hidden = false;
}
this.setState({ [name]: currState });
}
The way to prevent that was to use hideUnhide as a callback to the setState in editItem:
this.setState({ [className]: currState }, () =>
this.hideUnhide({ target: { name: className } })
);
and now everything functions as intended.

React getting state

I am currently creating my own mini-bbcode editor for my ReactJS application. I use functional components. In the TextEditor.jsx file I have the following code that handles the textarea.
const [bbcodeText, setBbcodeText] = useState("");
const update = (e) => {
setSelection(null);
setBbcodeText(e.target.value);
if (typeof props.onChange == 'function') {
props.onChange(e.target.value);
}
}
<textarea className="form-input forum__body-input" ref={textAreaRef} value={bbcodeText} onChange={update} />
The above works correctly, however I installed [emoji-mart] 1 to have access to the option to place emojis in the textarea, for which I use the following code:
const handleEmoji = (emoji) => {
let newText = bbcodeText + " " + emoji.native;
setBbcodeText(newText);
}
<Picker title="Emojis" emoji="grinning" set='apple' style={{width: '100%'}} onSelect={handleEmoji} />
The textarea updates correctly, the problem is that this TextEditor component accesses it from a parent component of the form:
const [bbcode, setBBcode] = useState();
const update = (v) => {
setBBcode(v);
};
const handlePost = async() => {
console.log(bbcode)
}
<TextEditor onChange={update} onSelect={handleEmoji} />
<button className="button purple" onClick={handlePost}>Agregar respuesta</button>
The problem occurs if I select an emoji and try to access the "bbcode" state, the result is an empty string, but if I write something after it, the emoji is already shown with the text at the end, that is, I need to fire the onChange to display the updated data.
What I need to know is a guide on how to get the updated status, if I only want to insert emojis in the textarea, for example.
In this case bbcode returns undefined if I only use "emojis" but if I write text it works correctly.
I'd suggest moving the onChange callback logic to an useEffect hook with a dependency on the bbcodeText state. This way anything that updates the bbcodeText state value will trigger the effect to invoke the onChange handler to update anything in the parent component.
const [bbcodeText, setBbcodeText] = useState("");
const update = (e) => {
setSelection(null);
setBbcodeText(e.target.value);
}
const handleEmoji = (emoji) => {
setBbcodeText(bbcodeText => bbcodeText + " " + emoji.native);
}
useEffect(() => {
if (typeof props.onChange == 'function') {
props.onChange(bbcodeText);
}
}, [bbcodeText]);

Can i search about some files stored in google bucket inside my react app?

I have a reaction app that stores some files in the google cloud " Bucket " so I wonder if I can search for some files stored in a 'Bucket' inside my React app which i don't know what is the exact name of it, Can I do that?
If yes, in what way?
if you have any tutorial, i will be appreciate.
What i mean by search is this list and filter:
thanks in advance.
What do you mean "search"? If you already know the name you want to find, you can try to open the file. If it fails, it either doesn't exist or you don't have permission to open it.
If you want to see if it exists before opening, this should point you in the right direction:
from google.cloud import storage
client = storage.Client()
blobs = client.list_blobs('your_default_bucket')
filenames = []
for blob in blobs:
filenames.append(blob.name)
print(filenames)
file_exists = 'my_file.csv' in filenames
print(f"file_exists: {file_exists}")
For this kind of cases it's better to use 3rd part libraries. One that could suit your need is react-autosuggest.
basic usage:
import Autosuggest from 'react-autosuggest';
// Imagine you have a list of languages that you'd like to autosuggest.
const files = [
{
name: 'file1'
},
{
name: 'file2'
},
...
];
// Teach Autosuggest how to calculate suggestions for any given input value.
const getSuggestions = value => {
const inputValue = value.trim().toLowerCase();
const inputLength = inputValue.length;
return inputLength === 0 ? [] : languages.filter(lang =>
lang.name.toLowerCase().slice(0, inputLength) === inputValue
);
};
// When suggestion is clicked, Autosuggest needs to populate the input
// based on the clicked suggestion. Teach Autosuggest how to calculate the
// input value for every given suggestion.
const getSuggestionValue = suggestion => suggestion.name;
// Use your imagination to render suggestions.
const renderSuggestion = suggestion => (
<div>
{suggestion.name}
</div>
);
class Example extends React.Component {
constructor() {
super();
// Autosuggest is a controlled component.
// This means that you need to provide an input value
// and an onChange handler that updates this value (see below).
// Suggestions also need to be provided to the Autosuggest,
// and they are initially empty because the Autosuggest is closed.
this.state = {
value: '',
suggestions: []
};
}
onChange = (event, { newValue }) => {
this.setState({
value: newValue
});
};
// Autosuggest will call this function every time you need to update suggestions.
// You already implemented this logic above, so just use it.
onSuggestionsFetchRequested = ({ value }) => {
this.setState({
suggestions: getSuggestions(value)
});
};
// Autosuggest will call this function every time you need to clear suggestions.
onSuggestionsClearRequested = () => {
this.setState({
suggestions: []
});
};
render() {
const { value, suggestions } = this.state;
// Autosuggest will pass through all these props to the input.
const inputProps = {
placeholder: 'Type a programming language',
value,
onChange: this.onChange
};
// Finally, render it!
return (
<Autosuggest
suggestions={suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
getSuggestionValue={getSuggestionValue}
renderSuggestion={renderSuggestion}
inputProps={inputProps}
/>
);
}
}
check a demo here also

How to catch remove event in react-select when removing item from selected?

first of all, thanks for awesome work. It makes a lot easier for me. Then here i want to catch remove event, how can I do that? I read the documentation and could not find remove event
I don't think they have an event for that. They just have the onChange.
So, to detect if some option was removed, you would have to compare the current state with the new values the onChange emitted.
Example:
handleOnChange(value) {
let difference = this.state.selected.filter(x => !value.includes(x)); // calculates diff
console.log('Removed: ', difference); // prints array of removed
this.setState({ selected: value });
}
render() {
return (
<div>
<Select
multi={this.state.multi}
options={this.state.options}
onChange={this.handleOnChange.bind(this)}
value={this.state.selected}
showNewOptionAtTop={false}
/>
</div>
);
}
Demo: https://codesandbox.io/s/7ymwokxoyq
React Select exposes a variety of eventListeners to you via props. The onchange function prop now has the following signature. (value: ValueType, action: ActionType).
Accordingly, you can get these events:
select-option, deselect-option, remove-value, pop-value, set-value, clear, create-option, through which you can handle creation and deletion.
Please refer to documentation
They don't have any event for that. I was facing the same problem but in my case, I was needing both the added and removed item. In case someone wants the both values
import 'difference' from 'lodash/difference'
this.currentTags = []
handleChange = (options) => {
const optionsValue = options.map(({ value }) => value)
if (this.currentTags.length < options.length) {
const addedElement = difference(optionsValue, this.currentTags)[0]
this.currentTags.push(addedElement)
console.log("addedElement",addedElement)
//call custom add event here
}
else {
let removedElement = difference(this.currentTags, optionsValue)[0]
console.log("removedElement", removedElement)
// call custom removed event here
let index = this.currentTags.indexOf(removedElement)
if (index !== -1) {
this.currentTags.splice(index, 1)
}
}
}
This code of snippet is working for me to know if any item is removed
I am calling handle change function on onChange event in reat-select
const [selectedGroups, setSelectedGroups] = useState([]);
const handleChange = (value) => {
const diifer = value.length <= selectedGroups.length;
if(diifer){
// item is removed
}
var arr = value.map((object) => object.value);
setSelectedGroups(arr);
};

Resources