Returning an arrow function to a component - reactjs

I have the arrow function below and I want to be able to return everything here to a component.
How do I return everything? Is it possible to use an export with this? The code below resides in
a js file.
const myTester = props => {
[
{
title: props.intl.formatMessage({
id: "ren_past_due"
}),
icon: (
<EventBusy
color="#cc2444"
style={style}
className="icon-box-icon-image material-icons"
/>
),
color: "#C60C30",
expiresBefore: today()
},
{
title: props.intl.formatMessage({
id: "ren_zerotothree_months"
}),
icon: (
<Today
color="#f2b826"
style={style}
className="icon-box-icon-image material-icons"
/>
),
color: "#F0AB00",
expiresAfter: today(),
expiresBefore: today().add(3, "months")
}
].map(item => {
if (item.expiresBefore) {
item.expiresBefore = item.expiresBefore.format("Y-MM-DD");
}
if (item.expiresAfter) {
item.expiresAfter = item.expiresAfter.format("Y-MM-DD");
}
return item;
});
};

Add a return in front of [ in line #2.
Shorthand to return arrays () => ([1, 2, 3]);.
Shorthand to return object () => ({ name: 'shorthand' });.

I'm not sure where you are using this 'javascript' file.
If wanted to use like like a NPM module then just simply do following:
module.exports = myTester;
When importing this file you will get this function
const test = require('./myTester.js');
// test is now test(props);
On front-end on older ways before rectJs and import this would had just worked by calling function myTester(props) and importing file via script.
<script type="text/javascript" src="/myTester.js"></script>
With react or new ways now it's
import myTester from 'myTester'

Related

Mocked Apollo Provider with React testing library is not returning data

I've been trying to fix this problem for a couple days and I'm still at a loss.
I have a React app that uses Apollo Client and I want to test one component with the React testing library. Omitting all the irrelevant details, my main file code looks like that:
const ComponentToTest = () => {
...
const { data, loading } = useComponentRespQuery({
variables: {
id: idValue
},
skip: !idValue
});
if (loading) {
return <>Loading</>;
}
...
}
And the test is written like that:
const componentRender = (mockGraphQLData: any, initialEntry: string = "") => render(
<MemoryRouter initialEntries={[initialEntry]}>
<MockedProvider mocks={[ mockGraphQLData ]} addTypename={false}>
<ComponentToTest />
</MockedProvider>
</MemoryRouter>
);
describe("ComponenToTest", () => {
it("should render a component to test", async() => {
componentRender(zoneMock);
const placeHolderValues = await screen.findByText(/Bla/i);
expect(placeHolderValues).toBeInTheDocument();
});
...
}
Where componentRespDocument is equal to
query GetResp($id: ID!) {
resp(id: $id) {
id
name
description
controls {
id
name
}
}
And my mock is written like that
export const componentRespMock = {
request: {
query: componentRespDocument
},
result: {
data: {
resp: {
id: "ID",
name: "NAME",
description: "DESCRIPTION",
controls: [
{
id: "CTRL_2",
name: "Second control"
}
]
}
}
}
};
I can confidently say that this way of testing worked before in my codebase. I found one way to make Apollo return the right value while testing, and it was to comment out all the code in useComponentRespQuery.
Anybody faced it before and knows how to fix it?
It is a bit hard to say without knowing what's under the hood in MockedProvider provider. However, in my experience with mocks they should always match (1:1), especially if it works when you comment out { variables: { id: idValue }, skip: !idValue } inside useComponentRespQuery.
I would suggest double checking zoneMock to make sure that it matches what you expect

How to clear all text in Slate.JS editor

For the life of me, I can't figure out how to clear all of the text in an Editor component from slate.js.
I have tried:
Transforms.delete(editor, {}); -> doesn't do anything
editor.deleteBackward("line"); -> only deletes one line, not all
I have also tried manually rerendering the editor component and that unfortunately doesn't update it to its initial state :(
I have been tearing through the slate js docs and can't find anything anywhere! If anyone has any ideas, would be very happy.
This is how editor is implemented:
const editor = useMemo(() => withHistory(withReact(createEditor())), []);
<Editable
key={stateKey}
onKeyDown={(event: any) => handleKeyDown(event)}
style={{ overflowWrap: "anywhere", width: "100%" }}
onPaste={(e) => {
if (e.clipboardData) {
if (e.clipboardData.files.length > 0) {
setFiles([...files, ...Array.from(e.clipboardData.files)]);
e.preventDefault();
}
}
}}
decorate={decorate}
renderLeaf={renderLeaf}
placeholder="What's happening?"
/>
You can mutate editor.children and set it to an "empty" value.
const editor = useMemo(() => withHistory(withReact(createEditor())), []);
editor.children = [
{
type: "paragraph",
children: [{ text: "" }]
}
];
Note: Slate needs a minimum of an array with one empty text node to render correctly.
You can use
import { Transforms } from "slate";
// useref
const editorRef = useRef()
if (!editorRef.current) editorRef.current = withHistory(withReact(createEditor()))
const editor = editorRef.current
const reset = () =>{
// loop delete all
editor.children.map(item => {
Transforms.delete(editor, { at: [0] })
})
// reset init
editor.children = [
{
type: "p",
children: [{ text: "" }]
}];
}

React Testing Library custom render function and async await

I have the following custom render function for my component.
It has two modes Create and Edit.
Create is synchronous and Edit is asynchronous.
The function is as follows:
const renderComponent = async (
scheduleId = "",
dialogMode = DialogMode.CREATE,
cohorts = MOCK_COHORT_LIST,
jobs = JOB_LIST,
availableForSchedule = domain === Domain.COHORT ? jobs : cohorts,
) => {
render(
<AddEditScheduleDialog
cohorts={cohorts}
jobs={jobs}
availableForSchedule={availableForSchedule}
scheduleToEdit={scheduleId}
handleToggleDialog={mockToggleDialog}
isDialogVisible={true}
domain={domain}
/>,
{
wrapper: queryWrapper,
},
);
if (dialogMode === DialogMode.EDIT) {
expect(screen.getByRole("progressbar")).toBeInTheDocument();
}
await waitFor(() =>
expect(screen.queryByRole("progressbar")).not.toBeInTheDocument(),
);
return {
header: screen.getByRole("heading", {
level: 1,
name: `schedules:addEditDialog.${domain}.${dialogMode}.title`,
}),
name: {
field: screen.getByTestId(`${domain}-name-field`),
button: within(screen.getByTestId(`${domain}-name-field`)).getByRole(
"button",
),
},
frequency: {
field: screen.getByTestId("schedule-frequency-field"),
input: within(
screen.getByTestId("schedule-frequency-field"),
).getByRole("textbox"),
helperText: `schedules:addEditDialog.form.frequency.helperText`,
},
};
};
Sometimes I get intermittent problems finding elements on the screen. Is this because the function returns before the progressbar has been awaited?
Is there a way i can wait for everything to be rendered prior to returning the screen elements that I need?
If your Edit mode is causing the progress bar to be displayed asynchronously you should wait for it by making use of findByRole e.g.
if (dialogMode === DialogMode.EDIT) {
expect(await screen.findByRole("progressbar")).toBeInTheDocument());
}
This is because find* queries use waitFor "under the hood".

Gutenberg Block Variation Picker not working

I'm trying to add the BlockVariationPicker like in the WordPress Github example:
import { useSelect } from '#wordpress/data';
import {
__experimentalBlockVariationPicker as BlockVariationPicker,
store as blockEditorStore,
} from '#wordpress/block-editor';
const MyBlockVariationPicker = ( { blockName } ) => {
const variations = useSelect(
( select ) => {
const { getBlockVariations } = select( blocksStore );
return getBlockVariations( blockName, 'block' );
},
[ blockName ]
);
return <BlockVariationPicker variations={ variations } />;
};
In my edit function I'm adding:
{ MyBlockVariationPicker }
The block variation picker does not show.
I have already registered my bloc variations with scope block:
registerBlockVariation(
'my/testimonial',
[
{
name: 'testimonial-1',
title: 'Testimonial 1',
scope: ['block'],
attributes: {
example: 'testimonial-1'
},
},
{
name: 'testimonial-2',
title: 'Testimonial 2',
scope: ['block'],
attributes: {
example: 'testimonial-2'
},
}
]
);
The block variations should show in { MyBlockVariationPicker } but the don't. Unfortunately there isn't much documentation about this. How can we render the variations of a block using the Block Variation Picker as shown in the Github example?
Both the Columns and Query block use __experimentalBlockVariationPicker and its a really nice component/UI and I agree, it there aren't many examples of how to use it, most likely as its still 'experimental' and still likely to change.
I found that both the Columns and Query blocks display the BlockVariationPicker by checking if the current block (by clientId) contains any InnerBlocks; if there are none, the BlockVariationPicker is shown. When using this component in your own block, you will need some attribute or property to check whether or not a variation has been selected.
I've put together a basic/working example using the structure of your my/testimonial block + variations and based on how the BlockVariationPicker is implemented in Columns block:
import { get } from 'lodash';
import { useSelect } from '#wordpress/data';
import { registerBlockType, registerBlockVariation, store as blocksStore } from '#wordpress/blocks';
import { useBlockProps, __experimentalBlockVariationPicker as BlockVariationPicker } from '#wordpress/block-editor';
// Create our own BlockVariationPicker
const MyBlockVariationPicker = ({ name, setAttributes }) => { // Note: We need "name" and "setAttributes" from edit() props
const { blockType, variations, defaultVariation } = useSelect(
(select) => {
const { getBlockVariations, getBlockType, getDefaultBlockVariation } = select(blocksStore);
return {
blockType: getBlockType(name),
defaultVariation: getDefaultBlockVariation(name, 'block'),
variations: getBlockVariations(name, 'block')
};
},
[name]
);
return <BlockVariationPicker
variations={variations}
icon={get(blockType, ['icon', 'src'])}
label={get(blockType, ['title'])}
onSelect={(nextVariation = defaultVariation) => {
if (nextVariation.attributes) {
setAttributes(nextVariation.attributes); // Use setAttributes to set the selected variation attributes
}
}}
/>;
};
// Register the Block Variations
registerBlockVariation(
'my/testimonial',
[
{
name: 'testimonial-1',
title: 'Testimonial 1',
icon: 'admin-comments', // Added icon so the variation is visibly different (optional)
scope: ['block'],
attributes: {
example: 'testimonial-1'
},
isDefault: true
},
{
name: 'testimonial-2',
title: 'Testimonial 2',
icon: 'admin-links',
scope: ['block'],
attributes: {
example: 'testimonial-2'
},
}
]
);
registerBlockType('my/testimonial', {
title: 'My Testimonial',
keywords: ['testimonial'],
icon: 'admin-post',
attributes: {
example: {
type: "string", // no default set, example is "undefined"
}
},
edit(props) {
const { attributes, setAttributes } = props;
// If example is undefined, show Variation Picker
if (attributes.example === undefined) {
return (
<MyBlockVariationPicker {...props} />
);
}
// Otherwise show the Editor
return (<div {...useBlockProps()}><h2>{attributes.example}</h2></div>);
},
save: ({ attributes }) => {
return <div {...useBlockProps.save()}><h2>{attributes.example}</h2></div>;
}
})
If you build the above javascript, the resulting block allows you to pick from the two variations on insertion:

Trying to extend rich markdown editor

I'm working on trying to extend this markdown editor library - which itself extends Prosemirror. I'd like to build my own Node which has the following schema:
attrs: {
id: number
}
and returns the following HTML
<div class=`story-block ${attrs.id}`>
<div contenteditable="false">
<button>Story</button>
</div>
<div>
<p>Text that we can edit</p>
</div>
</div>
The trick here is that since the editor saves to markdown. We need some way to understand this is a special type of node, a Story, but wrapping it in a unique set of characters, I'm using %%%. This is similar to how the library I'm extending using ::: as a signal for their Notice node
I am able to add a new Story node, and it renders my buttons and styles correctly. When I go to save everything saves correctly and I get markdown that looks like
%%%123 // the id
Whatever text I write
%%%
However, when I want to render than markdown back into DOM elements it fails and I get plain text in the document again. Specifically, the DOM looks like
<p>%%%123
Whatever text I write</p>
<p>%%%</p>
Code for the customer node is below. Has anyone successfully extended this library or can show me what I'm doing wrong? Thanks
import { wrappingInputRule } from "prosemirror-inputrules";
import toggleWrap from "../commands/toggleWrap"; //Rule file from library
import Node from "./Node";
export default class Story extends Node {
get name() {
return "container_story";
}
get schema() {
return {
attrs: {
id: {
default: "story",
},
},
content: "block+",
group: "block",
defining: true,
draggable: true,
parseDOM: [
{
tag: "div.story-block",
preserveWhitespace: "full",
contentElement: "div:last-child",
getAttrs: () => ({
id: "story",
}),
},
],
toDOM: node => {
node.attrs.id =
node.attrs.id === "story"
? Math.round(Math.random() * 10000)
: node.attrs.id;
console.log(node.attrs.id);
const button = document.createElement("button");
button.innerText = "Story";
button.id = node.attrs.id;
button.addEventListener(
"click",
(function () {
return function (e) {
alert(`Story ${e.target.id} clicked!`);
};
})(),
false
);
return [
"div",
{ class: `story-block ${node.attrs.id}` },
["div", { contentEditable: false }, button],
["div", { class: "content story-content" }, 0],
];
},
};
}
commands({ type }) {
return attrs => toggleWrap(type, attrs);
}
inputRules({ type }) {
return [wrappingInputRule(/^%%%$/, type)];
}
toMarkdown(state, node) {
state.write("\n%%%" + "story" + "\n");
state.renderContent(node);
state.ensureNewLine();
state.write("%%%");
state.closeBlock(node);
console.log(state);
}
parseMarkdown() {
return {
block: "container_story",
getAttrs: tok => {
console.log(tok.attrGet("id"));
({ id: tok.attrGet("id") });
},
};
}
}
It looks like you are missing a configuration for the markdown rules.
You can add a file in /src/lib/markdown, something like story.ts, with this content (copy/paste from notice.ts)
import customFence from "markdown-it-container";
export default function story(md): void {
return customFence(md, "story", {
marker: "%",
validate: () => true,
});
}
And in the rules.ts file (same dir), you use the new rule
import markdownit from "markdown-it";
...
import storyPlugin from "./story";
export default function rules({ embeds }) {
return markdownit("default", {
breaks: false,
html: false,
})
...
.use(noticesPlugin)
.use(storyPlugin);
}

Resources