Gutenberg Richtext generating "&#xFEFF" in editor - wordpress

I have used rich text element of Gutenberg, in editor i found this kind of entity coming when we are editing - https://prnt.sc/uqJZokTorIK_ , that is creating issue in layout, when we click its showing blank space.
/**
* WordPress dependencies
*/
import { __ } from "#wordpress/i18n";
import { RichText, useBlockProps } from "#wordpress/block-editor";
import { Platform } from "#wordpress/element";
export default function edit({ attributes, setAttributes }) {
const {circleHeadline,circleText} = attributes;
const blockProps = useBlockProps({
className: 'visual-circle__item',
});
const onTitleChange = (value) => {
const newTitle = { circleHeadline: value };
setAttributes(newTitle);
};
const onTextChange = (value) => {
const newText = { circleText: value };
setAttributes(newText);
};
return (
<li {...blockProps}>
<RichText
identifier="circleHeadline"
tagName="span"
className="visual-circle__title"
value={circleHeadline}
onChange={onTitleChange}
withoutInteractiveFormatting={true}
aria-label={__("Main Text")}
placeholder={__("Lorem")}
{...(Platform.isNative && { deleteEnter: true })} // setup RichText on native mobile to delete the "Enter" key as it's handled by the JS/RN side
allowedFormats={[""]}
/>
<RichText
identifier="circleText"
tagName="span"
className="visual-circle__text"
value={circleText}
onChange={onTextChange}
withoutInteractiveFormatting={true}
aria-label={__("Sub Text")}
placeholder={__("Nullam dictum eu pede")}
{...(Platform.isNative && { deleteEnter: true })} // setup RichText on native mobile to delete the "Enter" key as it's handled by the JS/RN side
allowedFormats={[""]}
/>
</li>
);
}
I have tried diffrent tag instead of span tag in rich text but issue is same.

I fixed this issue, i found that this character is coming in core blocks rich text element also, the issue in my code is i used span tag with display block css.

Related

Render dynamic Vue 3 slot content in StorybookJS

Using the example Storybook code at the bottom of this post, I expect to see a Primary button rendered containing the text, "Primary Button", but instead the button renders following code, verbatim:
(...args) => {
// If a user calls a compiled slot inside a template expression (#1745), it
// can mess up block tracking, so by default we disable block tracking and
// force bail out when invoking a compiled slot (indicated by the ._d flag).
// This isn't necessary if rendering a compiled `<slot>`, so we flip the
// ._d flag off when invoking the wrapped fn inside `renderSlot`.
if (renderFnWithContext._d) {
setBlockTracking(-1);
}
const prevInstance = setCurrentRenderingInstance(ctx);
const res = fn(...args);
setCurrentRenderingInstance(prevInstance);
if (renderFnWithContext._d) {
setBlockTracking(1);
}
if (true) {
devtoolsComponentUpdated(ctx);
}
return res;
}
How do I render Vue component slot content in Storybook?
The Storybook code:
// ./components/button/Button.stories.js
import AtxButton from './Button';
import ButtonTypes from './Button.types';
export default {
/* πŸ‘‡ The title prop is optional.
* See https://storybook.js.org/docs/vue/configure/overview#configure-story-loading
* to learn how to generate automatic titles
*/
title: 'Button',
component: AtxButton,
argTypes: {
default: {
description: 'The default Vue slot',
control: 'text'
}
}
};
//πŸ‘‡ We create a β€œtemplate” of how args map to rendering
const Template = (args, { argTypes }) => ({
props: Object.keys(argTypes),
components: { AtxButton },
setup() {
//πŸ‘‡ The args will now be passed down to the template
return { args };
},
template: `
<AtxButton v-bind="args">{{ args.default }}
</AtxButton>
`
});
//πŸ‘‡ Each story then reuses that template
export const Primary = Template.bind({});
Primary.args = {
intensity: ButtonTypes.Intensity.Primary,
default: 'Primary Button'
};
export const Secondary = Template.bind({});
Secondary.args = {
intensity: ButtonTypes.Intensity.Secondary,
default: 'Secondary Button'
};
I had syntax error in my component code. The Button component is created using a render function, which includes slot content by accessing this.slots.default(). I was getting a console error claiming "this.slots.default() is not a function", so I changed it to this.slots.default, assuming it might have been a getter.
That wasn't the right solution, of course. I changed it back to this.slots.default(), and then ensured that slot content was always included in the Storybook story. Now it works perfectly!

How to remove unwanted panels inside InspectorControls from core blocks in Gutenberg

If I want to extend the core block I can use Block Filters where I found a filter like editor.BlockEdit.
Example from doc:
const { createHigherOrderComponent } = wp.compose;
const { Fragment } = wp.element;
const { InspectorControls } = wp.blockEditor;
const { PanelBody } = wp.components;
const withInspectorControls = createHigherOrderComponent( ( BlockEdit ) => {
return ( props ) => {
return (
<Fragment>
<BlockEdit { ...props } />
<InspectorControls>
<PanelBody>My custom control</PanelBody>
</InspectorControls>
</Fragment>
);
};
}, 'withInspectorControl' );
wp.hooks.addFilter(
'editor.BlockEdit',
'my-plugin/with-inspector-controls',
withInspectorControls
);
It works, but now i would like to remove unwanted panels from core blocks. Unfortunately, the documentation does not contain any such example so I can't understand how to do this.
So how to remove unwanted panels inside InspectorControls from core blocks in Gutenberg?
Note: I know about the newest feature theme.json, but it has very limited functions and won't remove everything it needs

Nextjs - link to '/' without refreshing

// import Link from "next/link";
<Link href="/">Home</Link>
A link to / refreshes the page. Is there anything I can do to stop the page from refreshing, whenever the user navigates to the homepage?
The docs states this: When linking between pages on websites, you use the HTML tag. In Next js you use the Link Component from next/link to wrap the tag. it allows you to do client-side navigation to a different page in the application.
Just do
<Link href="/">
<a>
Home
</a>
</Link>
Use this hook and you'll never have to use Link ever again. Just use regular HTML anchors and they'll work as you would expect.
// useNextClickHandler.ts
import type { Router } from 'next/router'
import { useEffect } from 'react'
/**
* Place this in a Next.js's _app.tsx component to use regular anchor elements
* instead of Next.js's <code>Link</code> component.
*
* #param router - the Next.js router
*/
export default function useNextClickHandler(router: Router): void {
useEffect(() => {
async function onClick(event: MouseEvent) {
// Only handle primary button click
if (event.button !== 0) {
return
}
// Use default handling of modifier+click events
if (
event.metaKey ||
event.ctrlKey ||
event.altKey ||
event.shiftKey
) {
return
}
const anchor = containingAnchor(event.target)
// Only handle anchor clicks
if (!anchor) {
return
}
// Use default handling of target="_blank" anchors
if (anchor.target === '_blank') {
return
}
// If the link is internal, prevent default handling
// and push the address (minus origin) to the router.
if (anchor.href.startsWith(location.origin)) {
event.preventDefault()
await router.push(anchor.href.substr(location.origin.length))
}
}
window.addEventListener('click', onClick)
return () => window.removeEventListener('click', onClick)
}, [router])
}
function containingAnchor(
target: EventTarget | null
): HTMLAnchorElement | undefined {
let parent = target
while (
parent instanceof HTMLElement &&
!(parent instanceof HTMLAnchorElement)
) {
parent = parent.parentElement
}
return parent instanceof HTMLAnchorElement ? parent : undefined
}
// _app.tsx
export default function App({
Component,
pageProps,
router,
}: AppProps): JSX.Element {
useNextClickHandler(router)
Other alternative is by using next/router and utilise onClick event.
import Router from "next/router";
<a onClick={() => Router.push("/")}">
Home
</a>

Reactjs custom hook call infinite loop

I am creating my first ever self made web development project and have run into an infinite loop. The full project can be found on https://github.com/Olks95/my-dnd/tree/spellbook. So the question is: What causes the loop and how do I fix it?
(The loop happens somewhere in the 2nd item of the 'Playground' component when the ContentSelector - Spellbook is called. The custom hook useHandbook is called in Spellbook and continously calls the API, should obviously only happen once... refresh or click return to stop spamming )
From what I can tell the issue is not in the custom hook itself, as I have made several attempts to rewrite it and an empty dependency array is added to the end of the useEffect(). I will try to explain with example code here.
import { Component1, Component2, Component3 } from './ContentSelector.js';
const components = {
option1: Component1,
option2: Component2
option3: Component3
}
const Playground = (props) => {
const LeftItem = components['option1']
const MiddleItem = components['option2']
const RightItem = components['option3']
...
}
I wanted to be able to choose what content to put in each element and ended up making a ContentSelector component that has all the content components in one file, and individually imported/exported. This seems like a strange way to do it, but it was the only way I found to make it work. (Maybe the cause of the loop?) Since this is still fairly early on in the development the selection is hard coded. The item variables starts with a capital letter so I can later call them as components to render like so:
<LeftItem ...some properties... />
Playground then returns the following to be rendered:
return(
<React.Fragment>
<div className="container">
<div className="flex-item">
/* Working select-option to pass correct props to Component1 */
<div className="content">
<LeftItem ...some properties... />
</div>
</div
<div className="flex-item">
/* Currently the same selector that changes the content of the LeftItem */
<div className="content">
<MiddleItem ...some properties... />
</div>
</div>
/*RightItem follows the same formula but currently only renders "coming soon..." */
</div>
</React.Fragment>
)
The Content selector then has the three components where:
Component1: calls a custom hook that only runs once. The information is then sent to another component to render. All working fine.
Component2: calls a custom hook infinite times, but is expected to work the same way component 1 does...
Component3: Renders coming soon...
See Component1 and 2 below:
export const Component1 = (props) => {
const [ isLoading, fetchedData ] = useDicecloud(props.selectedChar);
let loadedCharacter = null;
if(fetchedData) {
loadedCharacter = {
name: fetchedData[0].Name,
alignment: fetchedData[0].Alignment,
/* a few more assignments */
};
}
let content = <p>Loading characters...</p>;
if(!isLoading && fetchedData && fetchedData.length > 0) {
content = (
<React.Fragment>
<Character
name={loadedCharacter.name}
alignment={loadedCharacter.alignment}
/* a few more props */ />
</React.Fragment>
)
}
return content;
}
export const Component2 = (props) => {
const [ fetchedData, error, isLoading ] = useHandbook('https://cors-anywhere.herokuapp.com/http://dnd5eapi.co/api/spells/?name=Aid')
let content = <p>Loading spells...</p>;
if(!isLoading && fetchedData) {
/* useHandbook used to return text to user in a string in some situations */
if(typeof fetchedData === 'string') {
content = (
<React.Fragment>
<p> {fetchedData} </p>
</React.Fragment>
)
} else {
content = (
<React.Fragment>
<Spellbook
/* the component that will in the future render the data from the API called in useHandbook */
/>
</React.Fragment>
)
}
}
return content;
}
I have been working on this issue for a few days and it is getting more confusing as I go along. I expected the mistake to be in useHandbook, but after many remakes it does not seem to be. The current useHandbook is very simple as shown below.
export const useHandbook = (url) => {
const [ isLoading, setIsLoading ] = useState(false);
const [ error, setError ] = useState(null);
const [ data, setData ] = useState(null);
const fetchData = async () => {
setIsLoading(true);
try {
const res = await fetch(url, {
method: "GET",
mode: 'cors'
});
const json = await res.json();
setData(json);
setIsLoading(false);
} catch(error) {
setError(error);
}
};
useEffect(() => {
fetchData();
}, []); //From what the documentation says, this [] should stop it from running more than once.
return [ data, error, isLoading ];
};
EDIT: I ran the chrome developer tools with the react extension and saw something that might be useful:
Image showing Component2 (Spellbook) run inside itself infinite times
You can modify your custom hook useHandbook's useEffect hook to have URL as dependency, since useEffect is similar to componentWillMount, componentDidUpdate and componentWillUnmount, in your case it is componentDidUpdate multiple times. So what you can do is.
useEffect(() => {
fetchData();
}, [url]);
Since there is no need to fetch data agin unless URL is changed
I found the mistake. Component2's real name was Spellbook which I had also called the rendering component that I had not yet made. It turns out I was calling the component from within itself.
Easy to see in the image at the edit part of the question.

How to add more advanced fields in each Gutenberg blocks?

Is there any way to extend the advanced field section in each Gutenberg blocks? I want to add more fields in each blocks. Please help.
New answer: Try this. I made this to add inline styling to my blocks. I used isAdmin to define wether admin or not, to prevent author or other users to add inline styles everywhere. In your
index.js
/**
* Inline Style component
*
* Shows a field for inline style only visible for admins
* and adds a style attribute to the save content of the block
*/
import './attributes.js';
import './inspector.js';
inspector.js
const { __ } = wp.i18n;
const { select } = wp.data;
const { createHigherOrderComponent } = wp.compose;
const { InspectorAdvancedControls } = wp.blockEditor;
const { TextareaControl } = wp.components;
const withInspectorControls = createHigherOrderComponent((BlockEdit) => {
return (props) => {
const { inlineStyle } = props.attributes;
const isAdmin = select( 'core' ).canUser( 'create', 'users' );
return (
<>
<BlockEdit {...props} />
{ isAdmin ?
<InspectorAdvancedControls key="inspector">
<TextareaControl
label={__('Inline styling', 'custom-blocks')}
help={ __('Notice: This inline style will override any other inline style generated by Gutenberg.', 'custom-blocks') }
value={inlineStyle}
onChange={inlineStyle => props.setAttributes({ inlineStyle })}
/>
</InspectorAdvancedControls>
: '' }
</>
);
};
}, 'withInspectorControl');
wp.hooks.addFilter('editor.BlockEdit', 'custom-blocks/inline-style/inspector', withInspectorControls);
attributes.js:
wp.hooks.addFilter('blocks.registerBlockType', 'custom-blocks/inline-style/attributes', function (settings, name) {
settings = window.lodash.assign({}, settings, {
attributes: window.lodash.assign({}, settings.attributes, {
inlineStyle: {
type: 'string',
default: "",
}
})
});
return settings;
});
wp.hooks.addFilter('blocks.getSaveContent.extraProps','custom-blocks/inline-style/inspector',function(props, name, atts){
if(atts['inlineStyle']!="")
return lodash.assign(props, { style: atts['inlineStyle'] });
return props;
});
Old answer:
As far as I know you can only add an input field to add and id to a block in the advanced field section.
However you can add custom sections to your inspector controls.
Basically there are two types of blocks: core blocks and custom blocks.
For custom blocks it is very simple, take a look at how to use a Panel
For core blocks, you need to use a filter:
blocks.registerBlockType: here you need to add attributes to the block you want to change
blocks.getSaveContent.extraProps: here you have to save the attributes
editor.BlockEdit: and here you can use the Panel to edit the block's inspector controls.

Resources