How do I properly deprecate Gutenberg block - wordpress

I have a custom Gutenberg block (https://pastebin.com/bV2k5Ekc) in which I display text, link and an image. I want to change it so so instead of saving the image URL as a background image of the container, to use an img tag. Unfortunately - I can't manage to create the deprecation correctly - I fail at assigning the attributes parameters in the deprecation:
From this:
const {
attributes: {
mediaURL,
boxTitle,
boxDescription,
linkText,
linkHref,
linkTitle
},
className,
} = props;
let boxClass = 'cta-box';
let contentDescription = '';
if (boxDescription.length) {
boxClass += ' cta-box-description';
contentDescription = (
<p>
{boxDescription}
</p>
)
}
return (
<div className={`cta-block-box ${className}`}>
<a
className="cta-box-link"
href={linkHref}
style={{ backgroundImage: "url(" + mediaURL + ")" }}
rel="noopener noreferrer"
>
<div className={boxClass}>
<h3>
{boxTitle}
</h3>
{contentDescription}
<span className="arrow">{linkText ? linkText : linkTitle}</span>
</div>
</a>
</div>
);
},
To this (I only change what's in the return statement):
return (
<div className={`cta-block-box ${className}`}>
<a
className="cta-box-link"
rel="noopener noreferrer"
>
<img className="cta-box-image" src={linkHref} alt=""/>
<div className={boxClass}>
<h3>
{boxTitle}
</h3>
{contentDescription}
<span className="arrow">{linkText ? linkText : linkTitle}</span>
</div>
</a>
</div>
);
Which of course broke the Gutenberg element. So I added a deprecate to the blog, as much as I could following the official Wordpress documentation:
deprecated: [
{
attributes: {...this.attributes},
save: (props) => {
const {
attributes: {
mediaURL,
boxTitle,
boxDescription,
linkText,
linkHref,
linkTitle
},
className,
} = props;
console.log('dep');
console.log(props);
let boxClass = 'cta-box';
let contentDescription = '';
if (boxDescription.length) {
boxClass += ' cta-box-description';
contentDescription = (
<p>
{boxDescription}
</p>
)
}
return (
<div className={`cta-block-box ${className}`}>
<a
className="cta-box-link"
style={{ backgroundImage: "url(" + mediaURL + ")" }}
rel="noopener noreferrer"
>
<div className={boxClass}>
<h3>
{boxTitle}
</h3>
{contentDescription}
<span className="arrow">{linkText ? linkText : linkTitle}</span>
</div>
</a>
</div>
);
},
}
],
After this the editor page crashes, and I get error message in console, that attributes is not defined (displaying incorrect row in the script file). This is the "after" script contents (https://pastebin.com/dVdLMx7N).
react-dom.min.js?ver=16.13.1:125 ReferenceError: attributes is not defined
at save (cta-box2.js?f66a:242)
at Wt (blocks.min.js?ver=9ed25ffa009c799f99a4340915b6dc6a:3)
at Qt (blocks.min.js?ver=9ed25ffa009c799f99a4340915b6dc6a:3)
at block-editor.min.js?ver=4378547cec8f5157a02ead3dfc5c65b2:12
at hooks.min.js?ver=50e23bed88bcb9e6e14023e9961698c1:2
at $r (blocks.min.js?ver=9ed25ffa009c799f99a4340915b6dc6a:3)
at blocks.min.js?ver=9ed25ffa009c799f99a4340915b6dc6a:3
at Ur (blocks.min.js?ver=9ed25ffa009c799f99a4340915b6dc6a:3)
at blocks.min.js?ver=9ed25ffa009c799f99a4340915b6dc6a:3
at Array.reduce (<anonymous>)
Any help would be greatly appreciated! I suspect I'm missing some small detail, but so far I've failed to locate it. And was not able to find relevant enough information on the web.
Thanks in advance!

There are two issues in your "after" script:
The attributes do not match (and the this is actually the window object): attributes: {...this.attributes} (see line 212).
So what you used with the attributes property on line 24, should also be used with the same property on line 212. (because you only changed the output, so the block attributes remain the same)
The save output/markup also do not match — in the "before" script, you've got href={linkHref}, but in the deprecated property of the "after" script, the save output did not have that href. (see this diff)
So make sure the attributes and save output match the ones in the old/"before" script, and the following is how your code would look like, but note that I only included the main parts that need to be fixed:
// Define the attributes and use it with the root "attributes" property and
// the one in the "deprecated" property.
const blockAttributes = {
mediaID: {
type: 'number'
},
mediaURL: {
type: 'string'
},
boxTitle: {
type: 'string',
default: ''
},
// ... the rest of the attributes here.
};
registerBlockType('hswp/test-box', {
title: __('Test Box', 'modula'),
// ... your code.
attributes: blockAttributes,
// ... your code.
deprecated: [
{
attributes: blockAttributes,
save: (props) => {
// ... your code.
return (
<div className={`cta-block-box ${className}`}>
<a
className="cta-box-link"
href={linkHref}
style={{ backgroundImage: "url(" + mediaURL + ")" }}
rel="noopener noreferrer"
>
... your code.
</a>
</div>
);
},
}
],
});
Additionally, note that the PlainText component doesn't (as of writing) have a property named tagName.

Related

How to create a page from the id parameter in the url

I'm trying to create a single page from the id that is passed as a parameter.
my routes structure:
When I pass the mouse over an item in my list I get the id of the firebase document so I need to create a page to show the data in the documents based on their ids.
http://localhost:3000/category/rent/541KqSMHpU17QuYLihFs
id:
541KqSMHpU17QuYLihFs
My listItem component:
<script>
export let listing
export let id
export let handleDelete
import DeleteIcon from '../../static/assets/svg/deleteIcon.svg'
</script>
<li class="categoryListing">
<a href={`/category/${listing.type}/${id}`} class="categoryListingLink">
<img src={listing.imgUrls[0]} alt={listing.name} class="categoryListingImg" />
<div class="categoryListingDetails">
<p class="categoryListingLocation">
{listing.location}
</p>
<p class="CategoryListingName">
{listing.name}
</p>
<p class="categoryListingPrice">
${listing.offer
? listing.discountedPrice.toString().replace(/\B(?=(\d{3})+(?!\d))/g, '.')
: listing.regularPrice.toString().replace(/\B(?=(\d{3})+(?!\d))/g, '.')}
{listing.type === 'rent' ? '/ por mês' : ''}
</p>
<div class="categoryListingInfoDiv">
<img src="/assets/svg/bedIcon.svg" alt="cama" />
<p class="categoryListingInfoText">
{listing.bedrooms > 1 ? `${listing.bedrooms} camas` : `${listing.bedrooms} cama`}
</p>
<img src="/assets/svg/bathtubIcon.svg" alt="banheiro" />
<p class="categoryListingInfoText">
{listing.bathrooms > 1
? `${listing.bathrooms} banheiros`
: `${listing.bathrooms} banheiro`}
</p>
</div>
</div>
</a>
{#if handleDelete}
<DeleteIcon
class="removeIcon"
fill="rgb(231, 76, 60)"
onClick={() => {
handleDelete(listing.id, listing.name)
}}
/>
{/if}
</li>
The important thing here is it:
<a href={`/category/${listing.type}/${id}`} class="categoryListingLink">
How do I make my [listingId] slug be the id page?
my [listingId].svelte so far:
<script>
import { page } from '$app/stores'
const listingId = $page.params.listingId
import { db } from '../../../../firebase.config.js'
// get the id parameter from the url
</script>
Happy new Year!!
I had a little trouble understanding your question at first.
As it stands now, your URIs are in the shape /category/id/[listingId], so http://localhost:3000/category/rent/541KqSMHpU17QuYLihFs won't get matched. What you need are URIs in the shape of /category/[id]/[listingId]. So you need to rename your id directory to [id] in order to make it dynamic.
You will then be able to retrieve the id the same way you do listingId:
<script>
import { page } from '$app/stores'
const { id, listingId } = $page.params
import { db } from '../../../../firebase.config.js'
// do stuff
</script>
With the above URL as an example, id will hold the value 'rent' and listingId will hold the value '541KqSMHpU17QuYLihFs'.
Hope this answers your question (and happy new year to you as well!)
Edit: a better, more explicit name for the [id] parameter would be [listingCategory] and would improve the readability of your code/the understanding of your intent.

nextjs <a> inside <Link> results in 2 history stacks

When I click on Hello, it redirects ok but to browse back to where i was by clicking back button, it requires 2 back button clicks. (probably because <Link> and <a> are triggered at the same time)
export default function Navigation() {
const router = useRouter()
const menus = [
{ key: 'key1', title: 'title1', clasName: 'class1' },
{ key: 'key2', title: 'title2', clasName: 'class2' },
]
return (
<div role="tablist">
{menus.map(({ key, title, className }) => (
<Link
href={{
pathname: router.pathname,
query: {
menu: key
}
}}>
<a
className={className}
role="tab">
<span>{title}</span>
</a>
</Link>
))}
</div>
)
}
I have to use <a> to apply className.
Only using <a> causes page to rerender even when href is same as current page.
What should I do to prevent 2 history stacks being inserted?
Thanks!
Apparently it was my custom popstate event listener that was pushing an extra stack into History. Thanks all for your comments

How to use search in react js and get the result in particular div?

while searching, the results should appear as a div like below :
i use jquery to search in table,how to get the result like above.
my component code is:
<div id="modaldash" style={{ display: (searching ? 'block' : 'none') }}>
<p className="font-weight-medium" id="name"> <img id="logo" className="logo" src={jessica} alt="pam-logo" /> Jessica James </p>
<button id="Addlist" onClick={this.onSubmitdata} className="btn info">{this.state.shown ? "Addded" : "Add to list"}</button>
<p id="mailid">jessicajames#gmail.com </p>
<p id= "address">Mountain view,Ave</p>
</div>
its just a static content for css. how to use search and get results like above.
export default function App() {
// considering the data object to search on name
const [searchedData, setSearchedData] = useState([]);
const users = [
{
name: "abc1",
emailid: "abc1#gmail.com",
address: "23rd main, 2nd street"
},
{
name: "adb2",
emailid: "abc2#gmail.com",
address: "23rd main, 2nd street"
},
{
name: "adc3",
emailid: "abc3#gmail.com",
address: "23rd main, 2nd street"
}
];
const handleSearch = event => {
const data = users.filter(
user => user.name.indexOf(event.target.value) !== -1
);
setSearchedData(data);
};
const showSearchedData = () => {
return searchedData.map(user => (
<div key={user.emailid}>
<p className="font-weight-medium" id="name">
{" "}
<img id="logo" className="logo" src="" alt="pam-logo" />
{user.name}
</p>
<button id="Addlist"> Added/ add to list</button>
<p id="mailid">{user.emailid} </p>
<p id="address">{user.address}</p>
</div>
));
};
return (
<div className="App">
<input type="text" onChange={handleSearch} />
<div id="modaldash">{searchedData.length > 0 && showSearchedData()}</div>
</div>
);
}
You can add CSS to make a look and feel like shown in image attached.
Check the working example here https://codesandbox.io/s/falling-sun-r3rim

Gutenberg setAttributes does not update my edit area

How can i make my HTML Elements in Gutenberg binded to an Array/Object?
Hi,
i am programming an Gutenberg Block now and just wanted to bind an Object to my Block but it does not update.
Currently i have an list of 's and wanted to be added automatically if i push the "Click me!" Button.
What it does is... If i push on that button, it pushes the new Element into the Array but the Elements are not added. If i click away (if the block loses focus), the elements are added.
What did i do wrong?
edit: props => {
const { setAttributes, attributes } = props;
let slides = props.attributes.slides;
const addSlide = function(event){
slides.push({ title : 'new' });
setAttributes({ slides: slides });
}
return [
<InspectorControls key="inspector">
<PanelBody
title={'Slides'}
initialOpen={true}
>
{slides.map((slide, i) =>
<li key={i}>
{slide.title}
</li>
)}
<Button isPrimary onClick={addSlide}>
Click me!
</Button>
</PanelBody>
</InspectorControls>,
<div className={ props.className } key='richtext'>
{slides.map((slide, i) =>
<li key={i}>
{slide.title}
</li>
)}
<Button isPrimary onClick={addSlide}>
Click me!
</Button>
</div>
];
}
I'm expecting the list elements to add dynamically while foxused.

Meteor: getting the HTML element which is using a global helper

Is it possible to know the HTML element which is invoking a global helper?
I have this blaze template:
<template name="tools">
<div>
<a id="pencil" class="{{toolIsActive}}">Pencil</a>
<a id="shape" class="{{toolIsActive}}">Shape</a>
<a id="poly" class="{{toolIsActive}}">Polygon</a>
<a id="arrow" class="{{toolIsActive}}">Arrow</a>
</div>
</template>
and so it'd be useful a helper like this:
UI.registerHelper('toolIsActive', function() {
return (this.id === currentTool) ? 'active' : '';
});
where I want this to be the invoking HTML element instead of template's data context.
Is there a way to access the element? I know I could use this.$('#pencil') but it's useless since the id it's exactly what I want to know.
You can work around this problem by passing the tool name as an argument for the helper:
<a id="pencil" class="{{toolIsActive 'pencil'}}">Pencil</a>
UI.registerHelper('toolIsActive', function(tool) {
return (tool === currentTool) ? 'active' : '';
});
Since this sort of helper is useful in many different parts of the application, you can make an universal one instead:
<a id="pencil" class="{{classIfEqual 'pencil' currentTool 'active'}}">Pencil</a>
UI.registerHelper('classIfEqual', function(a, b, className) {
return (a === b) ? className : '';
});
Another approach, which could make it easier to add more tools in the future:
<template name="tools">
<div>
{{#each tools}}
<a id="{{id}}" class="{{toolIsActive}}">{{humanName}}</a>
{{/each}}
</div>
</template>
Template.tools.helpers({
tools: [
{id: "pencil", humanName: "Pencil"},
{id: "shape", humanName: "Shape"},
{id: "poly", humanName: "Polygon"},
{id: "arrow", humanName: "Arrow"}
],
toolIsActive: function() {
return (this.id === currentTool) ? "active" : ""
}
});
You could potentially use that tools structure in multiple places, and then if you want to add more tools, you only have to add it in one place.

Resources