React Quill Default Value will not display with API call - reactjs

I am using react quill to set an editor. I want to prepopulate the editor with data from my API, However when I use the defaultValue it does not render. I can console.log the data from the API no problem. Here is the code.....
const GET_ISP_ENTRY = gql`
query IspListEntry($ispListEntryId: ID!) {
ispListEntry(id: $ispListEntryId) {
_id
displayName
contactFirstName
contactLastName
contactTitle
lastUpdated
onlineService
onlineAttn
address
city
state
zipCode
country
phoneNumber
extension
mobileNumber
faxNumber
email
website
referTo
notes
previous
}
}
`;
const UPDATE_ISP_ENTRY = gql`
mutation UpdateISPEntry($ispListEntryUpdateId: ID!, $input: UpdateISPEntry) {
ispListEntryUpdate(id: $ispListEntryUpdateId, input: $input) {
displayName
}
}
`;
const UpdateISPEntry = () => {
const [formValues, setFormValues] = useState();
const [urlId, setUrlId] = useState('');
const [notesFromAPI, setNotesFromAPI] = useState();
const [previousState, setPreviousState] = useState();
const [getIsp, { data, loading, error }] = useLazyQuery(GET_ISP_ENTRY, {
variables: {
ispListEntryId: urlId
},
onCompleted: () => {
setNotesFromAPI(data && data.ispListEntry.notes);
},
onError: () => {
toast.error(error);
}
});
console.log(loading ? 'Loading...' : notesFromAPI);
const [
submitValues,
{ data: successful, loading: successLoading, error: loadingError }
] = useMutation(UPDATE_ISP_ENTRY, {
onError: () => {
toast.error(`There was an error ${loadingError}`);
}
});
const params = useLocation();
const path = params.pathname;
const pathSplit = path.split('/')[2];
const [notesState, setNotesState] = useState();
useEffect(() => {
getIsp();
setFormValues(data && data.ispListEntry);
setUrlId(pathSplit);
}, [data, getIsp, pathSplit, formValues]);
const handleSubmit = () => {};
return (
<Fragment>
<div className='container p-4 parent-container'>
<ISPFormHeader />
<ISPFormHeaderPagename children='Update ISP Entry' />
<ISPForm
initialValues={data && data.ispListEntry}
enableReinitialize={true}
onSubmit={handleSubmit}
/>
<div className='editor-fields'>
<EditorComponent
defaultValue={notesFromAPI}
state={notesState}
onChange={setNotesState}
/>
</div>
<div className='editor-fields'>
<EditorComponent
placeholder='Enter Previous Notes Here'
state={previousState}
onChange={setPreviousState}
/>
</div>
</div>
</Fragment>
);
};
export default UpdateISPEntry;
and the Editor Component...
import React from 'react';
import ReactQuill from 'react-quill';
const modules = {
toolbar: [
[{ font: [] }],
[{ header: [1, 2, 3, 4, 5, 6, false] }],
['bold', 'italic', 'underline', 'strike'],
[{ color: [] }, { background: [] }],
[{ script: 'sub' }, { script: 'super' }],
['blockquote', 'code-block'],
[{ list: 'ordered' }, { list: 'bullet' }],
[{ indent: '-1' }, { indent: '+1' }, { align: [] }],
['link', 'image', 'video'],
['clean']
]
};
const EditorComponent = ({ placeholder, state, onChange, defaultValue }) => {
return (
<ReactQuill
defaultValue={defaultValue}
modules={modules}
theme='snow'
placeholder={placeholder}
state={state}
onChange={onChange}
/>
);
};
export default EditorComponent;

Related

React quill in nextjs and typescript custom image handler issue?

I am try to use react-quill in my typescript nextjs project. Here I am finding typing and ref issue that I can't solve. Please help me.
Here is code example-
import React, { useState, useRef } from 'react';
import dynamic from 'next/dynamic';
import { Container } from "#mui/material";
const ReactQuill = dynamic(import('react-quill'), {
ssr: false,
loading: () => <p>Loading ...</p>,
})
import 'react-quill/dist/quill.snow.css';
const Editor = () => {
const [value, setValue] = useState('');
const quillRef = useRef<any>();
const imageHandler = async () => {
const input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', 'image/*');
input.click();
input.onchange = async () => {
var file: any = input && input.files ? input.files[0] : null;
var formData = new FormData();
formData.append("file", file);
let quillObj = quillRef.current.getEditor();
};
}
const modules = {
toolbar: {
container: [
[{ font: [] }, { 'size': [] }, { 'header': [1, 2, 3, 4, 5, 6] }],
['bold', 'italic', 'underline', 'strike'],
[{ 'color': [] }, { 'background': [] }],
[{ 'script': 'sub' }, { 'script': 'super' }],
[{ 'header': 1 }, { 'header': 2 }, 'blockquote', 'code-block'],
[
{ list: 'ordered' },
{ list: 'bullet' },
{ indent: '-1' },
{ indent: '+1' },
],
[{ 'direction': 'rtl' }, { 'align': [] }],
['link', 'image', 'clean'],
],
'handlers': {
image: imageHandler
}
}
}
return (
<Container maxWidth="xxxl" disableGutters>
<ReactQuill
ref={quillRef} // Here I am finding an issue.
value={value}
modules={modules}
onChange={setValue}
placeholder="Start typing!"
/>
</Container>
);
};
export default Editor;
Here is CodeSandBox-
https://codesandbox.io/s/still-hill-xkb1pj
Can any one give me a proper typescript solutions.

React Quill - HTML Element with ID not Rendering Inside Editor

I am using React Quill (https://github.com/zenoamaro/react-quill) as a rich text editor in my React project.
Im running into an issue when inserting the below html span element to the editor with an ID:
<span id='incInsert'></span>
The value of the text editor is contained within React State and when console.logging state i can see the span element in there:
However, the span element doesnt exist when inspecting via chrome dev tools and thus in the DOM.
The reason why I need this element to exist in the DOM is because i need to use document.getElementById('incInsert') to insert HTML into the span element which is done later in my submit function.
TIA
I had the same problem, I solved it as follows:
import React, { useState, useRef } from "react";
import ReactQuill, { Quill } from "react-quill"; // ES6
import "react-quill/dist/quill.snow.css";
const Inline = Quill.import("blots/inline");
function MyComponent() {
const [content, setContent] = useState("");
const reactQuillRef = useRef(null);
const createElementWithClassName = () => {
class SpanBlock extends Inline {
static create() {
let node = super.create();
node.setAttribute("class", "spanblock");
node.setAttribute("id", "myId")
return node;
}
}
SpanBlock.blotName = "spanblock";
SpanBlock.tagName = "div";
Quill.register(SpanBlock);
const div = document.createElement("div");
var quill = new Quill(div);
quill.setContents([
{
insert: "hello",
attributes: {
spanblock: true,
},
},
]);
const result = quill.root.innerHTML;
console.log(result);
return result;
};
const buttonClick = () => {
const quill = reactQuillRef.current.getEditor();
const oldHtml = quill.root.innerHTML;
const newElement = createElementWithClassName();
const newHtml = oldHtml + newElement;
setContent(newHtml);
};
return (
<div>
<ReactQuill
ref={reactQuillRef}
modules={{
toolbar: [
[{ font: [] }, { size: ["small", false, "large", "huge"] }], // custom dropdown
["bold", "italic", "underline", "strike"],
[{ color: [] }, { background: [] }],
[{ script: "sub" }, { script: "super" }],
[{ header: 1 }, { header: 2 }, "blockquote", "code-block"],
[
{ list: "ordered" },
{ list: "bullet" },
{ indent: "-1" },
{ indent: "+1" },
],
[{ direction: "rtl" }, { align: [] }],
["link", "image", "video", "formula"],
["clean"],
],
}}
value={content}
onChange={(content) => {
setContent(content);
}}
/>
<button onClick={buttonClick}>click me</button>
</div>
);
}
export default MyComponent;

Filtering an array of products

I'm a newbie and I try to set up a search engine that will render the products, base on the value I type on the input.
With my code below, I tried that way but it seems that my logic isn't correct.
Could someone go through my code and check it out and can give me some insight afterward, please.
Thank you in advance.
import data from "./utils/data";
const App = () => {
const [searchValue, setSearchValue] = useState("");
const handleChange = (e) => {
setSearchValue(e.target.value);
};
return (
<div>
<SearchInput handleChange={handleChange} searchValue={searchValue} />
<Products data={data} searchValue={searchValue} />
</div>
);
};
const SearchInput = ({ searchValue, handleChange }) => {
return (
<div>
<input
type="text"
placeholder="Search specific item..."
value={searchValue}
onChange={handleChange}
/>
</div>
);
};
export default SearchInput;
function Products({ data, searchValue }) {
const [productsInfo, setProductsInfo] = useState([]);
useEffect(() => {
filteredProducts(data);
}, []);
const filteredProducts = (products) => {
if (searchValue.toLowerCase().trim() === "") {
setProductsInfo(products);
} else {
const seekedItem = productsInfo.filter(
(product) =>
product.name.toLowerCase().trim().includes(searchValue) ===
searchValue.toLowerCase().trim()
);
setProductsInfo(seekedItem);
}
};
const productsData =
productsInfo.length <= 0 ? (
<div>Loading...</div>
) : (
<div>
{productsInfo.map((product, index) => {
return (
<div
key={index}
style={{ backgroundColor: "grey", maxWidth: "300px" }}
>
<h4>{product.name}</h4>
<p>{product.category}</p>
<p> {product.price} </p>
<hr />
</div>
);
})}
</div>
);
return productsData;
}
export default Products;
const data = [
{
category: "Sporting Goods",
price: "$49.99",
stocked: true,
name: "Football",
},
{
category: "Sporting Goods",
price: "$9.99",
stocked: true,
name: "Baseball",
},
{
category: "Sporting Goods",
price: "$29.99",
stocked: false,
name: "Basketball",
},
{
category: "Electronics",
price: "$99.99",
stocked: true,
name: "iPod Touch",
},
{
category: "Electronics",
price: "$399.99",
stocked: false,
name: "iPhone 5",
},
{ category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7" },
];
export default data;
If you return empty array in second params in useEffect This function fired only once. Try that:
useEffect(() => {
filteredProducts(data);
}, [searchValue]);
.includes return true or false (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes)
Try that:
const filteredProducts = (products) => {
if (searchValue.toLowerCase().trim() === "") {
setProductsInfo(products);
} else {
const seekedItem = productsInfo.filter(
(product) =>
product.name.toLowerCase().trim().includes(searchValue)
);
setProductsInfo(seekedItem);
}
};

Filter/update already rendered chart.js in react.js

I'm new here, because I have decided to dive into programming, so I can fill free time between treatments in the hospital. I'm absolutely new in the programming field with no previous coding background.
The summary:
I am working on a simple page, where I fetch data from a Postgre database that is visualized using chart.js. The page is a built-in cube.js playground, using a Reactjs template. Currently, I can display various charts depending on my criteria. Like display monthly sales of a certain product in Australia. Or, I can display a second chart with daily sales in the countries I choose. Or ignore all sales that were in a certain currency. Right now, every new criterion means I have to use cube.js playground and generate a new chart on the page.
What I would like to achieve is to be able to filter already rendered charts (by a dropdown button outside the chart or inside the chart, it doesn't matter too much) and having the chart updated. Something like the pictures here, where the OP can filter charts based on the date, factory, etc.
I've tried Chart.js Example with Dynamic Dataset, chart.js tutorial on
Updating Charts and various others. But I can't seem to be able to implement any of those solutions in my code.
Here is my current code:
ChartRenderer.js
import React from "react";
import PropTypes from "prop-types";
import { useCubeQuery } from "#cubejs-client/react";
import Row from "react-bootstrap/Row";
import Spin from "react-bootstrap/Spinner";
import Col from "react-bootstrap/Col";
import { Statistic, Table } from "antd";
import { Line, Bar, Pie } from "react-chartjs-2";
const COLORS_SERIES = [
"#931F1D",
"#141446",
"#7A77FF",
];
const commonOptions = {
maintainAspectRatio: true,
};
const TypeToChartComponent = {
line: ({ resultSet }) => {
const data = {
labels: resultSet.categories().map((c) => c.category),
datasets: resultSet.series().map((s, index) => ({
label: s.title,
data: s.series.map((r) => r.value),
borderColor: COLORS_SERIES[index],
backgroundColor: COLORS_SERIES[index],
fill: false,
tension: 0.4,
})),
};
const options = { ...commonOptions };
return <Line data={data} options={options} />;
},
bar: ({ resultSet }) => {
const data = {
labels: resultSet.categories().map((c) => c.category),
datasets: resultSet.series().map((s, index) => ({
label: s.title,
data: s.series.map((r) => r.value),
backgroundColor: COLORS_SERIES[index],
fill: false,
})),
};
const options = {
...commonOptions,
scales: {
xAxes: [
{
stacked: true,
},
],
},
};
return <Bar data={data} options={options} />;
},
area: ({ resultSet }) => {
const data = {
labels: resultSet.categories().map((c) => c.category),
datasets: resultSet.series().map((s, index) => ({
label: s.title,
data: s.series.map((r) => r.value),
backgroundColor: COLORS_SERIES[index],
fill: true,
})),
};
const options = {
...commonOptions,
scales: {
yAxes: [
{
stacked: true,
},
],
},
};
return <Line data={data} options={options} />;
},
pie: ({ resultSet }) => {
const data = {
labels: resultSet.categories().map((c) => c.category),
datasets: resultSet.series().map((s) => ({
label: s.title,
data: s.series.map((r) => r.value),
backgroundColor: COLORS_SERIES,
hoverBackgroundColor: COLORS_SERIES,
borderColor: COLORS_SERIES,
hoverBorderColor: "white",
hoverOffset: 10,
})),
};
const options = { ...commonOptions };
return <Pie data={data} options={options} />;
},
number: ({ resultSet }) => {
return (
<Row
type="flex"
justify="space-around"
align="middle"
style={{ height: "100%" }}
>
<Col align="left">
{resultSet.seriesNames().map((s) => (
<Statistic value={resultSet.totalRow()[s.key]} />
))}
</Col>
</Row>
);
},
table: ({ resultSet, pivotConfig }) => {
return (
<Table
pagination={false}
columns={resultSet.tableColumns(pivotConfig)}
dataSource={resultSet.tablePivot(pivotConfig)}
/>
);
},
};
const TypeToMemoChartComponent = Object.keys(TypeToChartComponent)
.map((key) => ({
[key]: React.memo(TypeToChartComponent[key]),
}))
.reduce((a, b) => ({ ...a, ...b }));
const renderChart =
(Component) =>
({ resultSet, error }) =>
(resultSet && <Component resultSet={resultSet} />) ||
(error && error.toString()) || <Spin animation="grow text-primary" />;
const ChartRenderer = ({ vizState }) => {
const { query, chartType } = vizState;
const component = TypeToMemoChartComponent[chartType];
const renderProps = useCubeQuery(query);
return component && renderChart(component)(renderProps);
};
ChartRenderer.propTypes = {
vizState: PropTypes.object,
cubejsApi: PropTypes.object,
};
ChartRenderer.defaultProps = {
vizState: {},
cubejsApi: null,
};
export default ChartRenderer;
DashBoardPage.js
import React from "react";
import Col from "react-bootstrap/Col";
import DateRangePicker from 'react-bootstrap-daterangepicker';
import ChartRenderer from "../components/ChartRenderer";
import Dashboard from "../components/Dashboard";
import DashboardItem from "../components/DashboardItem";
const DashboardItems = [
{
id: 0,
name: "Sold by customers today",
vizState: {
query: {
measures: ["PostgreSqlTable.amount"],
timeDimensions: [
{
dimension: "PostgreSqlTable.added",
granularity: "day",
dateRange: "Today",
},
],
order: {},
dimensions: [],
filters: [
{
member: "PostgreSqlTable.operation",
operator: "contains",
values: ["Sell"],
},
],
},
chartType: "number",
},
},
{
id: 1,
name: "Bought by customers today",
vizState: {
query: {
measures: ["PostgreSqlTable.amount"],
timeDimensions: [
{
dimension: "PostgreSqlTable.added",
dateRange: "Today",
},
],
order: {},
filters: [
{
member: "PostgreSqlTable.operation",
operator: "contains",
values: ["Buy"],
},
],
},
chartType: "number",
},
},
{
id: 2,
name: "Money in the wallet",
vizState: {
query: {
measures: ["PostgreSqlTable.amount"],
timeDimensions: [
{
dimension: "PostgreSqlTable.added",
},
],
order: {
"PostgreSqlTable.amount": "desc",
},
dimensions: ["PostgreSqlTable.currency"],
filters: [
{
member: "PostgreSqlTable.currency",
operator: "equals",
values: ["EUR"],
},
],
},
chartType: "number",
},
},
{
id: 3,
name: "Monthly sales filtered by week",
vizState: {
query: {
measures: ["PostgreSqlTable.amount"],
timeDimensions: [
{
dimension: "PostgreSqlTable.added",
granularity: "week",
dateRange: "This month",
},
],
order: {
"PostgreSqlTable.amount": "desc",
},
dimensions: ["PostgreSqlTable.operation"],
filters: [
{
member: "PostgreSqlTable.operation",
operator: "notContains",
values: ["Register"],
},
],
limit: 5000,
},
chartType: "line",
},
},
{
id: 4,
name: "Countries with most customers",
vizState: {
query: {
measures: ["PostgreSqlTable.count"],
timeDimensions: [
{
dimension: "PostgreSqlTable.added",
},
],
order: {
"PostgreSqlTable.count": "desc",
},
dimensions: ["PostgreSqlTable.country"],
limit: 5,
},
chartType: "pie",
},
},
];
const DashboardPage = () => {
const dashboardItem = (item) => (
<Col className="col-4">
<DashboardItem title={item.name}>
<ChartRenderer vizState={item.vizState} />
</DashboardItem>
</Col>
);
const Empty = () => (
<div
style={{
textAlign: "center",
padding: 12,
}}
>
<h2>
No items added
</h2>
</div>
);
return DashboardItems.length ? (
<Dashboard dashboardItems={DashboardItems}>
{DashboardItems.map(dashboardItem)}
</Dashboard>
) : (
<Empty />
);
};
export default DashboardPage;
At this moment, I have no clue how to implement the filter in react.js+chart.js. I have also tried to update the array, but no success (I followed also this tutorial)
I would be most grateful for any help.
Thank you in advance, stay healthy.
Tatsu
I'd recommend using the <QueryBuilder/> component available in the Cube.js-React integration; this component provides a similar interface as that in the Developer Playground.

How to select the desired input field for filtering in React Data Grid

I am trying to write some tests using Cypress with Reactjs (both latest versions). It seems like there is nothing I can select these input fields of filtering because they all look the same. Is there a way to put id/class names to these fields? I am both new to React and Cypress. Thanks
https://codesandbox.io/s/w6jvml4v45?from-embed=&file=/src/index.js
import React, { useState } from "react";
import ReactDOM from "react-dom";
import ReactDataGrid from "react-data-grid";
import { Toolbar, Data } from "react-data-grid-addons";
import createRowData from "./createRowData";
import "./styles.css";
const defaultColumnProperties = {
filterable: true,
width: 120
};
const selectors = Data.Selectors;
const columns = [
{
key: "id",
name: "ID"
},
{
key: "title",
name: "Title"
},
{
key: "firstName",
name: "First Name"
},
{
key: "lastName",
name: "Last Name"
},
{
key: "email",
name: "Email"
},
{
key: "street",
name: "Street"
},
{
key: "zipCode",
name: "ZipCode"
},
{
key: "date",
name: "Date"
},
{
key: "jobTitle",
name: "Job Title"
},
{
key: "catchPhrase",
name: "Catch Phrase"
},
{
key: "jobArea",
name: "Job Area"
},
{
key: "jobType",
name: "Job Type"
}
].map(c => ({ ...c, ...defaultColumnProperties }));
const ROW_COUNT = 50;
const handleFilterChange = filter => filters => {
const newFilters = { ...filters };
if (filter.filterTerm) {
newFilters[filter.column.key] = filter;
} else {
delete newFilters[filter.column.key];
}
return newFilters;
};
function getRows(rows, filters) {
return selectors.getRows({ rows, filters });
}
function Example({ rows }) {
const [filters, setFilters] = useState({});
const filteredRows = getRows(rows, filters);
return (
<ReactDataGrid
columns={columns}
rowGetter={i => filteredRows[i]}
rowsCount={filteredRows.length}
minHeight={500}
toolbar={<Toolbar enableFilter={true} />}
onAddFilter={filter => setFilters(handleFilterChange(filter))}
onClearFilters={() => setFilters({})}
/>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Example rows={createRowData(50)} />, rootElement);
I would Use This specific attribute syntax to select them by key value:
cy.get(`input[key="${targetValue}"]`)
.then(element => {
//My code
})

Resources