How to insert a draft-js custom component/block - draftjs

I'm trying to insert my custom block to the editorState of draft-js's editor. I can't seem to find any detailed information on how to accomplish this.
Block Renderer:
const blockRendererFn = (contentBlock) => {
const type = contentBlock.getType();
if (type === 'CustomTestChipBlock') {
return {
component: CustomTestChipBlock,
editable: false,
props: {
foo: 'bar',
},
};
}
}
Block Render Map:
import { DefaultDraftBlockRenderMap } from "draft-js";
import { Map } from 'immutable';
const blockRenderMap = Map({
CustomTestChipBlock: {
element: 'div',
}
}).merge(DefaultDraftBlockRenderMap);
My custom block (material ui chip):
import { Chip } from "#mui/material";
const CustomTestChipBlock = (props) => {
const { block, contentState } = props;
const { foo } = props.blockProps;
const data = contentState.getEntity(block.getEntityAt(0)).getData();
console.log("foo: "+foo)
console.log("data: "+data)
return (
<Chip label="test" size="small"/>
)
}
Now my problem is when I try to insert my custom block. I assume my method of insertion must be wrong. I tried multiple insertion methods but due to lack of any detailed information on the subject, all of them ended up not even running the console.log inside my custom component.
Insertion:
const addChip = () => {
setEditorState(insertBlock("CustomTestChipBlock"));
}
const insertBlock = (type) => {
// This is where I can't find any detailed info at all
const newBlock = new ContentBlock({
key: genKey(),
type: type,
text: "",
characterList: List(),
});
const contentState = editorState.getCurrentContent();
const newBlockMap = contentState.getBlockMap().set(newBlock.key, newBlock);
const newEditorState = ContentState.createFromBlockArray(
newBlockMap.toArray()
)
.set("selectionBefore", contentState.getSelectionBefore())
.set("selectionAfter", contentState.getSelectionAfter());
return EditorState.push(editorState, newEditorState, "add-chip");
};

Related

React Query hook call from orval

I use orval to create interfaces, services and it also create React Query hooks. I have a hook that I cant figur out how to use. It wants some UseMutationOptions togheter with my data.
export const useGetUserContext = <TError = ErrorType<unknown>, TContext = unknown>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof getUserContext>>,
TError,
{ data: UserContextRequest },
TContext
>;
request?: SecondParameter<typeof customInstance>;
}) => {
const { mutation: mutationOptions, request: requestOptions } = options ?? {};
const mutationFn: MutationFunction<Awaited<ReturnType<typeof getUserContext>>, { data: UserContextRequest }> = (
props,
) => {
const { data } = props ?? {};
return getUserContext(data, requestOptions);
};
return useMutation<Awaited<ReturnType<typeof getUserContext>>, TError, { data: UserContextRequest }, TContext>(
mutationFn,
mutationOptions,
);
};

Uncaught (in promise) TypeError: hobbies is not iterable at createHobby when using Prisma and Postgresql

So I'm very new to Prisma, and actually also to React. My Postgresql database works, but I'm trying to show the stored data in my application. My very simple table in the schema file looks like this:
model Hobby {
id Int #id #default(autoincrement())
title String
}
I'm using useContext to distribute my createHobby functionality, this is what the context file looks like.
export async function getServerSideProps() {
const hobbies: Prisma.HobbyUncheckedCreateInput[] = await prisma.hobby.findMany();
return {
props: {initialHobbies: hobbies},
};
}
export const HobbyContext = createContext({})
function Provider({ children, initialHobbies }){
const [hobbies, setHobbies] = useState<Prisma.HobbyUncheckedCreateInput[]>(initialHobbies);
const createHobby = async (title) => {
const body: Prisma.HobbyCreateInput = {
title,
};
await fetcher("/api/create-hobby", {hobby : body});
console.log(hobbies);
const updatedHobbies = [
...hobbies,
body
];
setHobbies(updatedHobbies);
const contextData = {
hobbies,
createHobby,
}
return (
<HobbyContext.Provider value={contextData}>
{children}
</HobbyContext.Provider>
);
};
export default HobbyContext;
export {Provider};
Here I get the following error Uncaught (in promise) TypeError: hobbies is not iterable at createHobby. Which refers to the const updatedHobbies = [...hobbies, body];
For more context, I have a HobbyCreate.tsx which creates a little hobby card that renders the title of the hobby, which is submitted with a form.
function HobbyCreate({updateModalState}) {
const [title, setTitle] = useState('');
const {createHobby} = useHobbiesContext();
const handleChange = (event) => {
setTitle(event.target.value)
};
const handleSubmit = (event) => {
event.preventDefault();
createHobby(title);
};
return (
...
<form onSubmit={handleSubmit}></form>
...
)
I can't really figure out what is going wrong, I assume somewhere when creating the const [hobbies, setHobbies] and using the initialHobbies.
I don't think you're using the Context API correctly. I've written working code to try and show you how to use it.
Fully typed hobby provider implementation
This is a fully typed implementation of your Provider:
import { createContext, useState } from 'react';
import type { Prisma } from '#prisma/client';
import fetcher from 'path/to/fetcher';
export type HobbyContextData = {
hobbies: Prisma.HobbyCreateInput[]
createHobby: (title: string) => void
};
// you could provide a meaningful default value here (instead of {})
const HobbyContext = createContext<HobbyContextData>({} as any);
export type HobbyProviderProps = React.PropsWithChildren<{
initialHobbies: Prisma.HobbyCreateInput[]
}>;
function HobbyProvider({ initialHobbies, children }: HobbyProviderProps) {
const [hobbies, setHobbies] = useState<Prisma.HobbyCreateInput[]>(initialHobbies);
const createHobby = async (title: string) => {
const newHobby: Prisma.HobbyCreateInput = {
title,
};
await fetcher("/api/create-hobby", { hobby: newHobby });
console.log(hobbies);
setHobbies((hobbies) => ([
...hobbies,
newHobby,
]));
};
const contextData: HobbyContextData = {
hobbies,
createHobby,
};
return (
<HobbyContext.Provider value={contextData}>
{children}
</HobbyContext.Provider>
);
}
export default HobbyContext;
export { HobbyProvider };
Using HobbyProvider
You can use HobbyProvider to provide access to HobbyContext for every component wrapped inside it.
For example, to use it in every component on /pages/hobbies your implementation would look like:
// /pages/hobbies.tsx
import { useContext, useState } from 'react';
import HobbyContext, { HobbyProvider } from 'path/to/hobbycontext';
export default function HobbiesPage() {
// wrapping the entire page in the `HobbyProvider`
return (
<HobbyProvider initialHobbies={[{ title: 'example hobby' }]}>
<ExampleComponent />
{/* page content */}
</HobbyProvider>
);
}
function ExampleComponent() {
const { hobbies, createHobby } = useContext(HobbyContext);
const [title, setTitle] = useState('');
return (
<div>
hobbies: {JSON.stringify(hobbies)}
<div>
<input
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<button onClick={() => createHobby(title)}>Create hobby</button>
</div>
</div>
);
}
Similarly, to make the context available throughout your entire website, you can use HobbyProvider in
/pages/_app.tsx.
Using getServerSideProps
To retrieve the initialHobbies from the database, your getServerSideProps would look something like this:
// /pages/hobbies.tsx
import type { Hobby } from '#prisma/client';
export async function getServerSideProps() {
// note: there is no need to use `Hobby[]` as prisma will automatically give you the correct return
// type depending on your query
const initialHobbies: Hobby[] = await prisma.hobby.findMany();
return {
props: {
initialHobbies,
},
};
}
You would have to update your page component to receive the props from getServerSideProps and set initialHobbies on HobbyProvider:
// /pages/hobbies.tsx
import type { InferGetServerSidePropsType } from 'next';
export default function HobbiesPage({ initialHobbies }: InferGetServerSidePropsType<typeof getServerSideProps>) {
return (
<HobbyProvider initialHobbies={initialHobbies}>
<ExampleComponent />
</HobbyProvider>
);
}
Note your page component and getServerSideProps function have to be exported from the same file

AG Grid: Framework component is missing the method getValue() Vue3 Composition API with expose

I'm currently using ag-grid to render data and it works fine untill I try to edit cells using my custom cellEditorFramework component:
export default defineComponent({
name: 'LinesViewVersionEditor',
props: ['params'],
setup(props, { expose }) {
const value = ref(props.params.value)
const versionOptions = ref([])
const changedValue = ref(false)
const client = new Client({ baseURL: settings.ClientBaseUrl })
const getValue = function () {
console.log('getValue')
return value.value
}
const updateValue = function (value: { version: number; entitySlug: string; entityVersionPk: number }) {
props.params.api.stopEditing()
changedValue.value = true
}
versionOptions.value = [
{
value: value.value,
label: value.value?.version.toString()
}
]
...some code here
expose({
value,
getValue
})
return () => (
<Select
showArrow={false}
class={'ant-select-custom'}
value={value.value?.version}
options={versionOptions.value}
onChange={ value => { updateValue(value) } }
onClick={ async () => {
versionOptions.value = await getChildVersions(
client,
...args
)
}}
/>
)
}
})
As you can see I'm returning some TSX, so I'm forced to use Vue3 { expose } to return method to the parent component with agGrid table. And it has no access to exposed method & value. I tried to make different method in "methods" property of class component options and it worked as supposed. In ag-grid docs written that I can simply return getValue in setup() function but it doesn't work for me for no visible reason. Thank you in advance for help.

Rendering a menu in vue 3 after ajax method

I've gotten this menu to work without filtering it, but now I'm doing an ajax request to filter out menu items the user isn't supposed to see, and I'm having some trouble to figure out how to set the resulting menu data, the line that is not working is commented below:
<script>
import { ref } from 'vue';
import axios from 'axios';
var currentSelected = 'device_access';
var menuData = [
{
text: 'Device Access',
id: 'device_access',
children: [
{
text: 'Interactive',
link: '/connection_center'
},{
text: 'Reservation',
link: '/reserve_probe'
}, {
text: 'Reservation Vue',
link: '/reservation.html'
}
]
}, {
text: 'Automation',
id: 'automation',
show: ['is_mxadmin', 'can_schedule_scripts'],
children: [
{
text: 'Builder',
link: '/builder',
},{
text: 'Execution Results',
link: '/test_suite_execution_results'
},
]
}
];
function hasMatch(props, list) {
var match = false;
for (var i=0; i < list.length && !match; i++) {
match = props[list[i]];
}
return match;
}
export default {
name: 'Header',
setup() {
const cursorPosition = ref('0px');
const cursorWidth = ref('0px');
const cursorVisible = ref('visible');
//the menu is zero length until I get the data:
const menu = ref([]);
return {
menu,
cursorPosition,
cursorWidth,
cursorVisible
}
},
created() {
let that = this;
axios.get('navigation_props')
.then(function(res) {
var data = res.data;
var result = [];
menuData.forEach(function(item) {
if (!item.show || hasMatch(data, item.show)) {
var children = [];
item.children.forEach(function (child) {
if (!child.show || hasMatch(data, child.show)) {
children.push({ text: child.text, link: child.link });
}
});
if (children.length > 0) {
result.push({ text: item.text,
children: children, lengthClass: "length_" + children.length });
}
}
});
//continues after comment
this is probably the only thing wrong, I've run this in the debugger and I'm getting the
correct data:
that.$refs.menu = result;
since the menu is not being rebuilt, then this fails:
//this.restoreCursor();
})
.catch(error => {
console.log(error)
// Manage errors if found any
});
},
this.$refs is for template refs, which are not the same as the refs from setup().
And the data fetching in created() should probably be moved to onMounted() in setup(), where the axios.get() callback sets menu.value with the results:
import { onMounted, ref } from 'vue'
export default {
setup() {
const menu = ref([])
onMounted(() => {
axios.get(/*...*/).then(res => {
const results = /* massage res.data */
menu.value = results
})
})
return {
menu
}
}
}
I finally figured out the problem.
This code above will probably work with:
that.menu = result;
You don't need: that.$refs.menu
You can't do it in setup because for some reason "that" is not yet defined.
In my working code I added a new method:
methods: {
setMenuData: function() {
this.menu = filterMenu();
},
}
And "this" is properly defined inside them.

postgres/bookshelf fetchAll() withRelated (getting multiple relations)

I'm trying to do a fairly complicated (at least for me)API request that involves getting an array of reports. The report object itself has nested objects and so I am trying to pull the related data at the same time. The report object looks like this:
reports: { loading: false, error: null, data: [{id:'', date:'',
student_id:'',
feeding:[],diapering:[], nap:[], meds:[], playTime:[], comments:[],
supplies:[]}] }
I've figured out how to get one report with multiple related tables, but I haven't been able to figure out the syntax for getting an array of reports each with their own related data.
this is what I have at the moment:
exports.getReport = (date) => {
console.log(id)
return Reports.where(date)
.fetchAll({
withRelated: ['feeding', 'comment', 'diapering', 'nap', 'meds', 'playTime', 'supplies']
})
.then(report => {
const meds = report.related('meds')
const nap = report.related('nap')
const feeding = report.related('feeding')
const diapering = report.related('diapering')
const supplies = report.related('supplies')
const playTime = report.related('playTime')
const comm = report.related('comment')
const medsList = meds.map(med => {
return med.attributes
})
const napList = nap.map(n => {
return n.attributes
})
const feedingList = feeding.map(feed => {
return feed.attributes
})
console.log(feedingList)
const diaperList = diapering.map(diaper => {
return diaper.attributes
})
const suppliesList = supplies.map(supply => {
return supply.attributes
})
const playTimeList = playTime.map(play => {
return play.attributes
})
const commentList = comm.map(com => {
return com.attributes
})
return reports
const reports = report.models.map(rep => {
return rep.attributes
})
return [feedingList, commentList, napList, playTimeList,
suppliesList, diaperList, medsList, reports]
})
.catch(err => {
console.log(err)
})
}
this syntax works with a
.fetch()
, but not with the
fetchAll()
I'm getting a
report.related is not a function error