Developing a Team Members Block
Introduction
In this lecture, we are going to start working on our next block.
Overview of the Block
In my browser, I have a copy of the static version. We are going to develop a team members block. Clients will be able to add a list of team members. Each team member will have a profile, picture, name, bio, and links to their social media pages.
Interestingly, we are not going to be working on a single block. Developing this type of UI will involve creating two blocks. The first block will act as a container. The second block will represent each team member. If that doesn’t make sense, it will.
Multiple Blocks for UI
During the plugins development, it’s not uncommon to develop multiple blocks for creating a single interface. In fact, it can be easier to break a UI into multiple blocks. By doing so, we can reduce clutter.
Registering Blocks
Let’s try registering both blocks. In the resource section of this lecture, I provide a link to a gist with the starter files for the team members group block. As usual, I want you to add these files to your project under a new directory called Team Members Group. Pause the video and give it a try.
File Structure
Welcome back. I’ve added the files to my project in the source/block’s directory. I have a new folder called Team Members Group. Inside this folder, there are three new files. Let’s go through them.
BLOCK JSON File
The first file is called BLOCK JSON. This file has the typical setup for a block. The name of our block is called Team Members Group. This block will act as a container for the team members.
Block Attributes
Let’s look at the attributes for this block. We are storing two attributes called columns and image shape. Designing blocks can be a challenge. If you’re designing nested blocks, things can become even more difficult.
Responsibility of a Block
A great approach to designing blocks is to ask yourself: What responsibility should a block have? By thinking about this question, you will be able to make decisions easier.
Modifying Columns
For example, let’s think about our block’s attributes. We’re going to allow users to modify the columns in our block. Clients can show between 2 to 4 members in a row. Should this setting appear on the outer or inner block?
In my opinion, this attribute should appear on the outer block, aka the container. This type of setting will affect the overall performance of the blocks. It doesn’t make sense to configure the columns of individual blocks.
Image Shape Attribute
There’s another attribute for manipulating the image shape. A single team member will have a profile picture. Wouldn’t it make sense to manipulate the image shape on each team member from a glance? It does; however, what if the client has 20 team members?
They would need to modify the same setting 20 times for all members to have the same image shape. By adding this setting to the parent block, the setting must be changed once, and we can pass on this setting to the children blocks.
On the other hand, nothing is stopping you from adding this setting to the individual blocks. For this course, we are going to apply it from the parent block. There’s nothing else to say about the BLOCK JSON file.
Index JS File
Let’s take a look at the index JS file. I’ve provided a lot of code but nothing new to you. Let’s scroll to the edit function.
Edit Function Overview
In the edit function, we’re extracting the image shape and column attributes. These attributes will be useful later. Inside the templates, we’re adding the inspector controls component for adding form controls to the sidebar.
Sidebar Controls
The controls inside the sidebar will manipulate the columns and image shape. Let’s take a closer look at the columns. We’re adding a component called Range Control.
The Range Control Component adds a slider for changing a numeric value within a specific range. Like most input components, we can add a label, set the current value, and add an event listener for updating the current value.
In this case, we’re manipulating the columns attributes. Clients will be able to change the number of columns within our block. The component below this control is called Select Control.
Select Control
It’s a component we’re already familiar with. This component will manipulate the image shape. We’re going to provide three shapes. You’re more than welcome to add additional shapes. If you decide to add more shapes, you will need to add CSS classes for them.
Future Discussions
In a future lecture, we will discuss how the shapes can be applied to the image. Let’s scroll to the bottom of the file. We will come across the save function. It’s been a while, but we are going to be using client-side rendering for our block.
Rendering Decisions
There isn’t a reason to use server-side rendering. We aren’t going to be relying on dynamic data. It makes sense to use client-side rendering. The save function is very barebones.
CSS File Overview
I don’t usually do this, but let’s look at the main CSS file. Most of the CSS is straightforward. If you scroll through the file, I’ve added a section for adding styles to the editor.
Issues with Columns
I ran across some issues with getting the columns to work in the editor. I’ve had to add better selectors. You may run into the same problems, so feel free to steal these selectors for your project. That’s about it for this block.
Moving Forward
We’re not finished yet. In the resource section of this lecture, I provide a link to the starter files for the team member block. As stated before, we are going to be creating two blocks for creating the UI.
Creating Team Member Block
In your project, create a new folder called Team Member. The files from this gist should be created from within the new directory. Pause the video and create the files.
Reviewing Team Member Files
Welcome back. Let’s review the files together. There’s not much to say about them. There are dozens of lines of code, but nothing we haven’t encountered before.
Block JSON for Team Member
For example, the BLOCK JSON file has a few attributes. There are more attributes than we’re used to. We’re going to store the name, title, bio, social handles, and image data of a member.
Edit and Save Functions
Next, let’s head down over to the index JS file. Inside this file, we have the edit and save functions. We do not need a server to render this UI inside the edit function.
Sidebar Controls for Team Member
The block will have a control in the sidebar for editing the image’s alt attributes, the block’s name, or rendering the name, title, image, bio, and social handles. The name, title, and bio attributes will be modifiable via the rich text component.
Focus Areas
I prepared this component for these locations. Our focus is going to be on allowing clients to modify their image and social media handles on our blocks already.
Registering the Team Member Blocks
The next step is to register them. Open the register block. Start PHP file. At the bottom of the block’s array, add two arrays for the team members, group and team member blocks. Neither block needs an options array.
Testing the Blocks
Let’s try using these blocks. I’m going to open the experimental page in the Gutenberg Editor. If you have blocks from previous sections, you can safely remove them after doing so. Try adding the team member blocks to the page.
Adding Support for Inner Blocks
Introduction
In this lecture, we are going to add support for inner blocks. A block can act as a container for additional blocks.
Example in the Gutenberg Editor
Let’s look at an example inside the Gutenberg editor. I’m going to add the buttons block to the page. After adding this block, we can open the list view sidebar for an overview of the blocks on the page.
Buttons Block Structure
The Buttons BLOCK is not a standalone block. In reality, there are two blocks for creating this UI. We have a container for adding multiple buttons. The button block will generate a single button.
Common Relationship in Custom Blocks
It’s not uncommon to create this type of relationship for our custom blocks. WordPress has a component for supporting inner blocks. In the resource section of this lecture, I provide a link to the documentation page for the inner blocks components.
Inner Blocks Component
This component will add an area for inserting custom blocks from within a block. In addition, a button will become available for inserting new blocks. Feel free to read the documentation after finishing this lecture.
Customization Options
This component offers a few options for customizing the behavior. Let’s give it a try.
Modifying the Team Members Group Block
In your editor, open the index js file for the team members group block. Next, let’s import the inner blocks component from the WordPress/block editor package.
Editing the Block’s Template
After importing this component, scroll to the block’s edit function. We are going to modify the block’s templates inside the div tag with the block prompts variable and the inner blocks component as a self-closing tag.
Inner Blocks Component Functionality
The inner component works out of the box. We don’t need to provide configuration. However, I should give you a warning: WordPress does not allow a block to have multiple locations for inner blocks. A block may only have one inner blocks component.
Updating the Save Function
Before testing our code, the save function must be updated to include the inner block’s content component inside the div tag. The inner block’s component has two variations. The first variation will provide a UI for inserting new blocks, whereas the inner blocks content component will not add this UI. It will simply render the blocks inserted into the parent block.
Testing the Code
Let’s try testing our code. Refresh the page in the browser. On this page, I’m going to add the team members group block. As you can see, we have an empty block upon selecting the block. A plus icon will appear.
Rendering the Plus Icon
Gutenberg is rendering this icon on our behalf. We can click this icon to open a dropdown. By using a single component, we can easily add blocks to our custom block. I’m going to add two copies of the team member block.
Refining Block Behavior
So far, the component works as intended. However, we should refine the behavior. Firstly, we should limit the type of blocks that can be inserted into the current block by default. Currently, all blocks are allowed for the sake of consistency. Let’s limit the blocks to the team member block.
Configuring the Inner Blocks Component
Luckily, the inner blocks component is configurable. We can adjust the behavior to address both issues. Let’s head back to the code editor. Scroll to the edit function.
No Changes Needed for Save Function
These changes do not need to be applied to the save function. The inner block’s content component will not handle this behavior.
Orientation Property
In the inner blocks component, there’s a property called orientation. This option can have one of two values: vertical or horizontal. The vertical option will add the inserter below the block. The horizontal option will add the inserter on the sides of a block. Let’s set this option to horizontal.
Allowed Blocks Property
Next, let’s add a property called allowed blocks. The value for this option can be an array of valid block names. In our case, we’re going to limit the inner blocks component to the utility plus slash team member block.
Final Testing
After adding these options, let’s try testing our block again. If we were to add blocks to the team members group block, WordPress will automatically insert the team member block.
Benefits of Configuration
We don’t get the option of inserting other blocks. It’s quicker than selecting a long list of blocks. We are running short on time.
Adding a Template to the Inner Blocks Component
Introduction
We are going to add a template to the inner blocks component. At the moment, the team members group block is always empty. Users must manually add blocks.
Assumptions About User Behavior
We can safely assume that users may want to add blocks upon inserting this block. WordPress makes the same assumption for its own blocks. For example, let’s try adding the buttons block to the page. After adding this block, the parent block will have a single child block.
Specifying a Template
Upon insertion, we can force WordPress to add blocks to the inner blocks component by specifying a template. Let’s give that a try in your editor.
Modifying the Index JS File
Open the index js file for the team members group block. Scroll to the edit function. Let’s modify the properties of the inner blocks component by adding a property called Template.
Defining the Template
The value for this property will be an array of blocks to add to the inner blocks component. In this array, we can specify a block by adding another array. Let’s try adding the team member block.
Adding Team Member Block
The first item in the array must be the full name of the block, which is “udemy/team-member.” The second item in the array can be an object of attributes to override from the block. This value is completely optional.
Setting Attribute Values
If you don’t provide values for a block’s attributes, WordPress will use the default values. For this example, let’s add an object. Set the name attribute to “John Doe.” The title attribute will have a value of “CEO of Udemy.” Lastly, the bio attribute will be set to the following value: “This is an example of a bio.”
Adding More Team Member Blocks
Just so that our block isn’t alone, let’s add two more copies of the team member block without attribute values. This will be useful for testing our code.
Template Lock Property
There’s another property worth looking at for this block. We aren’t going to need it, but it’s still nice to know. I’m referring to a property called Template Lock. We may want users to change the templates by adding or moving blocks.
User Permissions
By default, users will be able to remove the blocks or add new ones. We can set this property to “all” to completely prevent blocks from being removed or added. Alternatively, we can set this property to “insert” to prevent removal while still allowing users to add new blocks.
Freedom for Users
This option won’t be necessary. We are going to give users complete freedom over the blocks.
Refreshing and Testing the Block
Let’s try refreshing the page in the browser. Insert the team members group block. After doing so, three blocks have been added to the inner blocks component.
Result of Template Implementation
The first block will have the attribute values from the templates, whereas the other blocks will fall back to their default values. Adding a template is a great way to push your users in the right direction.
Adding Column Support for Our Blocks
Introduction
We are going to add column support for our blocks. Currently, the children blocks are stacked vertically. The orientation should be modified to horizontal stacking.
Fixing the Orientation Issue
We can fix this issue with CSS. In my editor, I’m viewing the main CSS file for this block. We are going to use CSS grids for creating columns in this file. I have three classes called columns, two columns, three columns, and four columns.
Toggling Between Classes
We’re going to allow users to toggle between these classes. Let’s open the index JS file.
Modifying Block Attributes
Our block has an attribute called columns. This attribute can be a number between two and four. I’ve already added a control for modifying this block from the edit function. Check out the inspector controls component.
Implementing Range Control
We have a component called range control for modifying the columns attributes. It has the usual properties for creating an input. However, it’s missing two properties as it stands. Our CSS only supports two, three, or four columns. The range control component will allow users to select numbers beyond this range.
Limiting Range Values
We can limit the range by adding two properties called Min and Max. Let’s set these properties to two and four, respectively. This will limit the range of values.
Applying Classes to Templates
Moving on, let’s begin applying the classes to the templates. We must apply classes to the root element. Luckily, we do not need to directly modify the root elements. The root element has the block props variable.
Using Block Props Function
The useBlockProps function accepts an object of properties to add to the root element. Let’s add the class name property. The value for this property will be a template string with the following value: columns
.
Dynamic Class Interpolation
The class will be dynamic. We are going to interpolate the columns attributes into the class name. Let’s make a copy of this object. The same classes must be applied to the save function; paste the object into the useBlockProps
save function.
Accessing Column Attributes
One more thing to note: the save function does not have access to the columns attributes above the save function. The structure of the attributes object holds the columns attributes.
Testing in the Browser
Let’s refresh the page and add the team members group block to the page. This time, the blocks are arranged horizontally. Our columns are working as expected.
Modifying Columns from the Sidebar
On the sidebar, we can modify the columns by moving the slider. As we do so, the blocks should adjust accordingly. If you’re interested in having additional columns, you’re more than welcome to modify the classes and range.
Additional Notes
There’s one more thing I want to mention. In an earlier lecture, we set the orientation to horizontal. By doing so, we can hover our mouse between the blocks to insert new blocks. Previously, this element would appear underneath the blocks. This time, it appears in between the blocks.
Recommendation on Orientation
Setting the orientation is optional, but I recommend setting it for a better user experience.
Adding Image Upload Functionality to the Team Member Block
Introduction
We are going to add the option of uploading an image to the team member block. We have the option of writing the entire logic, but that’s a lot of work. It would take hours to create an interface for managing images. Fortunately, WordPress has a component for handling uploads called the media placeholder component.
Documentation Reference
In the resource section of this lecture, I provide a link to the documentation page for this component. This component will generate an entire interface for managing and uploading files. We can implement this component from within our blocks to leverage its features.
Implementing the Media Placeholder Component
Let’s give it a try. In your editor, open the indexed JS file for the team member block. The media placeholder component can be imported from the WordPress slash block editor package.
Updating the Block
Next, let’s update our block to use this component. Scroll to the edit function’s templates. Inside the templates, we have an empty image tag. Below this tag, let’s render the media placeholder component. In the future, we are going to toggle the appearance of this component. If an image has been selected, this component will be hidden. Otherwise, the image tag will be hidden. For now, both elements will appear in our block. It’s going to create an awkward UI, but that’s perfectly fine.
Configuring the Media Placeholder Component
On this component, we can configure the behavior with properties. The media placeholder component will list all media files uploaded to our site. Media files are not limited to images. Sites can store audio, video, and PDF files.
Limiting Accepted File Types
Let’s limit the type of files that can appear in this block. First, we are going to set the accepted types property to an array of file types.
Understanding MIME Types
If you’re not familiar with a MIME type, let’s quickly talk about them. Typically, files can be identified by their extension. For example, PHP files have a .php extension. There is another way of identifying a file called a MIME type. Unlike extensions, MIME types are not visible from within the file name. They’re stored as metadata in a file.
Format of MIME Types
MIME types follow a specific format. It’s the category followed by the actual file type. For example, if we had a tag image, the MIME type would be image/pag. The image portion of the MIME type would be the category. The category and file type are separated with a forward slash character. In the resource section of this lecture, I provide a link to a list of common MIME types.
Supporting Image MIME Types
Generally, MIME types can share a category for our block. We are going to support MIME types that belong to the image category. Let’s update our code to use this MIME type. Head back to the editor. In the array, we can provide a list of MIME types. Let’s add the image category.
Omitting Specific File Types
It would be tedious to list every image MIME type. Luckily, we can omit the file type by providing the category. If we provide the category, WordPress will permit files within this category. However, if you want to limit the type of files that can be uploaded, feel free to add the file type. For example, let’s say the block can only have PNG files. We can update the array to contain the image/png MIME type. I’m going to revert this value to the image category.
Adding Accepted Property
As stated before, we’re going to allow any image to be uploaded. This property will affect the type of files that will appear from within the media uploader. In addition to this property, we must add the accepted property. Users will have the option of uploading a file. They do not need to select an existing file from the media library.
Handling Uploaded Files
If they decide to upload a file, we must add the accepted property to set the type of file uploaded to the server. Let’s set this value to the following: image/*.
Using Wildcards in MIME Types
Once again, we’re using a MIME type. However, we’re adding the asterisk character to act as a wildcard. By doing so, the file upload will be marked as an image. After making those changes, let’s add a few more properties.
Adding an Icon to the Component
The next property is called icon. We can add an icon to help the component stand out. I’m going to set the icon to “add many users.” This icon is from the official WordPress icon set called Dash Icons. We can set this value to the name of the icon from this set.
Handling Upload Events
The purpose of adding the media placeholder is to allow users to add an image for a team member. Let’s move on to adding functions for handling uploads. First, let’s add the onSelect property. The value for this property will be an arrow function that accepts the upload as an argument called image.
Logging Selected Image Information
Inside the body of the function, log the message. The onSelect property is an event. This event gets emitted after an image has been selected or uploaded. We are given the image’s information via an argument. This information will be helpful for updating the block’s appearance.
Adding Error Handling
Before testing our code, there’s another event worth adding called onError. This event is emitted during errors. For example, an upload can fail. Let’s pass in an arrow function to handle the event. In this function, we can accept the error as an argument called error. In the body of the function, we can log the error with the console.error function.
Difference Between Log and Error Functions
Normally we would log messages with the log function. However, the error function is another function for logging messages. The difference is that the message will have a red background. It’s not required to log errors with the error function, but it can add clarity. If you want, you can swap this function with the log function.
Testing the Code
Let’s try testing our code. Refresh the page. Next, add the team members group block. In the team member block, WordPress has generated a UI for uploading a file or selecting a media file with a few lines of code. We’re given this entire interface. Overall, using the media placeholder component is the best way to handle uploads.
Selecting an Image
Let’s select an image. After selecting an image, the details are logged in the console. We are given various pieces of information from the name of the file to the file size. With this information, let’s begin updating our block to render the image.
Adding a Custom Image Size for the Team Member Block
Introduction
We are going to add a custom image size for the team member block. Each team member will have a profile image, and we want these images to have dimensions of 56×56 pixels. By default, WordPress creates four copies of images in different sizes: original, large, medium, and thumbnail. While these sizes are optimized for general use, none of them are suitable for our block.
Benefits of Custom Image Sizes
Creating a custom image size can enhance performance. Smaller images lead to smaller file sizes, resulting in faster page loads. Therefore, it’s a good idea to create a custom image size for our block.
Registering a Custom Image Size
Let’s get started. Open the index.php
file in your editor. New image sizes must be registered during a specific hook called after_setup_theme
.
Using the add_action
Function
We will call the add_action
function with this hook. This hook runs after the theme files have been loaded. As a reminder, plugins run earlier than themes, so we need to wait for the theme to be ready before registering a new size.
Defining the Callback Function
Next, let’s set the name of the function to up_setup_theme
. This function doesn’t exist yet, so let’s create it inside a new file. Create a file called setup.php
in the included directory. Inside this file, define the up_setup_theme
function.
Registering the Image Size
After defining the function, we will run the add_image_size
function. This function registers a new image size for images uploaded to the site. It has four arguments: name, width, height, and crop value.
- Name: Set the name of our custom size to
team_member
. - Width and Height: Set both to 56 pixels.
- Crop Value: This argument configures how the image will be cropped. Cropping helps maintain the image’s aspect ratio. For our purpose, cropping from the center is ideal. We can achieve this by passing
true
.
Example Code
Here’s what the code in setup.php
might look like:
phpCopy codefunction up_setup_theme() {
add_image_size('team_member', 56, 56, true);
}
add_action('after_setup_theme', 'up_setup_theme');
Installing the Regenerate Thumbnails Plugin
After registering the image size, we should ensure that our changes take effect in the WordPress admin dashboard. Navigate to the “Add New” page for plugins and search for a plugin called Regenerate Thumbnails. This plugin allows WordPress to create new image sizes, including our custom size for all images.
Using the Plugin
After installing and activating the plugin, you can regenerate thumbnails for existing images. This is important because any existing images will not automatically have the new size available. Under the “Tools” menu, click on “Regenerate Thumbnails.” This will give you the option to regenerate all thumbnails.
Important Note
The regeneration process may take some time, especially if your site has many existing images. Be patient while it runs.
Testing the Custom Image Size
Once the regeneration is complete, let’s test our new image size. Go to the experimental page and add the team members group block. Try adding an image and ensure that your developer tools are open.
Inspecting the Image Object
Inside the console, log the image’s info. Look for a property called sizes
. This property will be an object containing available sizes for the image. While WordPress will include its own default sizes, you should also see our custom size listed.
Fixing the Custom Image Size Issue
Introduction
We will resolve the problem of our custom image size not appearing in the Gutenberg editor. The core reason is that WordPress does not automatically include custom values in its responses, particularly when using the REST API. If we want our custom image size to be available, we need to explicitly add it to the API response.
Understanding Filter Hooks
To include our custom image size, we will use a filter hook. Unlike action hooks that execute code without returning a value, filter hooks require a return value. They allow plugins to modify existing values.
Setting Up the Filter Hook
- Open the
index.php
File: Locate the section where we’ve been adding hooks. - Add a New Hook: At the bottom, add a new hook for
image_size_names_choose
using theadd_filter
function.
Naming the Function
We’ll name the function up_custom_image_sizes
. Now, let’s create a new file called image-sizes.php
inside the includes folder, where we will define this function.
Defining the Function
Inside image-sizes.php
, we will define the up_custom_image_sizes
function. This function will accept an array of image sizes provided by WordPress and return a modified array that includes our custom size.
Merging Arrays
We’ll use the array_merge
function to combine the existing sizes with our custom size.
- Accept the Argument: The function will accept the sizes as an argument.
- Create a New Array: This array will include our custom size formatted as follows:
- Key: Custom size name (e.g.,
team_member
) - Value: Human-readable name (e.g.,
Team Member
)
- Key: Custom size name (e.g.,
Example Code
Here’s what the code in image-sizes.php
might look like:
phpCopy codefunction up_custom_image_sizes($sizes) {
return array_merge($sizes, [
'team_member' => __('Team Member', 'text-domain'),
]);
}
add_filter('image_size_names_choose', 'up_custom_image_sizes');
Testing the Code
After implementing the code:
- Refresh the Experimental Page: Go to the experimental page in your WordPress editor.
- Add the Team Members Group Block: Add the block if it’s not already present.
- Select an Image: When you attempt to add an image, open the console panel in your developer tools.
Verifying the Custom Size
Check the logged object for a property called sizes
. This time, our custom size should be listed alongside the default image sizes.
Handling Image Selection in the Team Member Block
Introduction
We’ll implement the functionality to display the selected image in the team member block after it’s uploaded. This will involve updating the block’s attributes to store relevant image information and conditionally rendering the image based on user interaction.
Steps to Implement Image Handling
1. Update the index.js
File
Open the index.js
file for the team member block to make the necessary updates.
2. Handling Image Selection
Scroll to the media placeholder
component and locate the function passed to the onSelect
event. This function is triggered when a user uploads or selects an image.
Update Attributes
Inside this function, we will use the setAttributes
function to update three key attributes:
- Image ID: Store the ID of the selected image for future reference.
- Image Alt Text: Store the alt text for accessibility.
- Image URL: Store the URL of the custom image size.
Here’s how you can implement it:
javascriptCopy codeconst onSelectImage = (image) => {
setAttributes({
imageID: image.id,
imageAlt: image.alt,
imageURL: image.sizes.team_member.url,
});
};
3. Bind Attributes to the Image Element
After updating the attributes, we need to display the selected image. Locate the existing img
tag in your code.
Bind the src
and alt
Attributes
Bind the src
attribute to the imageURL
and the alt
attribute to the imageAlt
:
javascriptCopy code<img
src={attributes.imageURL}
alt={attributes.imageAlt}
/>
4. Conditionally Render the Image
To avoid rendering an empty image tag before an image is selected, we’ll use a conditional rendering approach:
javascriptCopy code{attributes.imageURL && (
<img
src={attributes.imageURL}
alt={attributes.imageAlt}
/>
)}
5. Toggle the Media Placeholder
We also want the media placeholder to disappear after an image is selected. We can achieve this by setting the disableMediaButtons
property on the media placeholder
:
javascriptCopy code<MediaPlaceholder
onSelect={onSelectImage}
disableMediaButtons={!!attributes.imageURL} // Converts to boolean
/>
6. Testing the Code
After making these changes, refresh the experimental page in your browser:
- Add the team members group block.
- Upload an image through the media placeholder.
- Verify that the image appears and the media placeholder disappears.
Allowing External Image Links in the Team Member Block
Introduction
We’ll extend our team member block to allow users to add images from external sources via a URL. This gives clients flexibility in managing their images, especially if they prefer not to upload them directly to their site.
Steps to Implement URL Handling
1. Update the index.js
File
Open the index.js
file for the team member block to configure the media placeholder component.
2. Modify the Media Placeholder Component
Locate the media placeholder
component and look for the property onSelectURL
. This event is triggered when a user submits a URL for an image.
Define the onSelectURL
Function
You need to pass an arrow function to handle this event:
javascriptCopy code<MediaPlaceholder
onSelect={onSelectImage}
onSelectURL={(url) => {
setAttributes({
imageID: '', // Resetting image ID
imageAlt: '', // Resetting image alt
imageURL: url, // Setting the URL attribute
});
}}
/>
3. Update Attributes
Inside the onSelectURL
function, you will:
- Reset the
imageID
andimageAlt
attributes to their original values. - Set the
imageURL
attribute to the provided URL.
This allows the block to now accept image URLs alongside uploaded images.
4. Testing the Code
After implementing these changes, refresh the Gutenberg Editor:
- Add the team members group block.
- In the media placeholder, you should see an option for adding an image via URL.
- Enter an external image URL and submit it.
- Verify that the image appears within your block.
Understanding the Blob URL
What is a Blob?
- Blob stands for Binary Large Object. It is a way for browsers to represent file data, such as images or videos, while they are being uploaded.
- When you upload an image, WordPress creates a blob to hold the file temporarily, which allows you to preview the image before it’s fully uploaded.
Why the Error Occurs
When uploading an image, the returned object does not contain properties like sizes
, which leads to the error message stating that the sizes
property is undefined. This is expected behavior since the image is not yet available on the server.
Steps to Fix the Error and Preview the Blob
1. Update the onSelect
Function
Open the index.js
file for the team member block and locate the onSelect
event handler for the media placeholder component. We will modify this function to handle both the blob and the standard image object.
2. Check for Blob URL
Modify the function to check if the uploaded image is a blob:
javascriptCopy codeconst onSelectImage = (image) => {
if (image && image.url) {
// If the image is a blob URL, set the image URL directly
setAttributes({
imageID: image.id || '',
imageAlt: image.alt || '',
imageURL: image.url || '',
});
} else if (image && image.blob) {
// Handle blob URLs for previews
setAttributes({
imageID: '',
imageAlt: '',
imageURL: URL.createObjectURL(image.blob),
});
}
};
3. Update the Rendering Logic
We need to ensure that we can render images regardless of whether they’re blobs or standard images. Below the media placeholder component, add logic to display the image:
javascriptCopy code{imageURL && (
<img
src={imageURL}
alt={imageAlt}
style={{ width: '56px', height: '56px' }} // Adjust size as needed
/>
)}
4. Testing the Implementation
After making these changes, refresh the Gutenberg editor:
- Add the team members group block.
- Upload an image. You should now see the image displayed using the blob URL until the upload completes.
Handling blob images
1. Importing the Required Function
First, we need to import the isBlobURL
function from the WordPress blob package. This function will help us determine if a URL is a blob.
javascriptCopy codeimport { isBlobURL } from '@wordpress/blob';
2. Modifying the onSelect
Function
Next, let’s update the onSelect
function to accommodate blob URLs. Open your index.js
file and scroll to the onSelect
function.
Step-by-Step Modifications:
- Initialize a New Variable: At the top of the function, create a variable called
newImageURL
and initialize it tonull
.
javascriptCopy codelet newImageURL = null;
- Check for Blob URLs: Use a conditional statement to check if the image URL is a blob.
javascriptCopy codeif (isBlobURL(image.url)) {
newImageURL = image.url; // Blob URL
} else {
newImageURL = image.sizes?.team_member?.url || image.media_details?.sizes?.team_member?.source_url; // Regular image URL
}
3. Setting Attributes
After determining the correct URL, update the attributes to reflect the selected or uploaded image.
javascriptCopy codesetAttributes({
imageID: image.id || '',
imageAlt: image.alt || '',
imageURL: newImageURL,
});
4. Testing the Implementation
Now, let’s test the changes:
- Refresh the Gutenberg Editor: Open your editor and add the team members group block.
- Upload an Image: Try uploading a new image and observe if it renders correctly.
- Select an Existing Image: Verify that you can also select an image from the media library.
5. Checking for Errors
- Open the developer console and ensure that no errors are thrown during the upload process.
- Inspect the image element in the Elements panel to confirm that the
src
attribute does not start withblob
. It should now reference an actual image stored in the database.
Adding a Spinner to the Image Upload
Introduction
We are going to add a spinner to the image. After uploading an image, the user doesn’t know that the image is uploading in their eyes. The image has already been uploaded. The moment it appears in the block, we should add a spinner to indicate the image is uploading.
Exercise
Try adding a spinner as an exercise. If the URL to the image is a blob, the spinner should appear. Otherwise, the spinner can be hidden. This means the spinner must be conditionally rendered. Pause the video and give it a try. Good luck.
Solution Overview
Welcome back. Hopefully, you were able to add the spinner. If not, that’s fine. Let’s go through the solution together in your editor, open the index JS file for the team member block.
Step 1: Import the Spinner Component
First, we must import the spinner component. This component can be imported from the WordPress Flash Components package.
Step 2: Modify the Image Tag
Next, let’s scroll to the image tag. This tag is responsible for displaying the profile image of a team member. Below this tag add an expression. Let’s add the following expression: isBlobURL(imageURL) && <Spinner />
. The condition is the isBlobURL
function. If this function returns true, the image is being uploaded. Therefore, we are going to render the spinner component.
Testing the Code
That’s it. We’ve added a spinner. Let’s try testing our code. In your editor, try uploading a new image after adding the team members group block to the page. As the image is uploading, the spinner component will appear before disappearing. Once it’s gone, the image has been uploaded.
Note on Spinner Visibility
The spinner may appear briefly. If you’re on a fast internet connection, it can be difficult to view the spinner.
CSS for the Spinner
Before ending this lecture, I want to quickly show you the CSS for the spinner. The spinner component generates an SVG tag. This tag has the component spinner class applied to it by default. The spinner does not use absolute positioning. If you were to add the spinner component next to an image, the spinner would not appear above it. We must apply absolute positioning to achieve this effect.
Fixing an Issue with the Team Member Block
Introduction
We are going to fix an issue with our team member block. This issue may not be apparent from the beginning until it becomes too late. Let me show you what I mean in the browser.
Demonstration of the Issue
I’m viewing the experimental page for this demonstration. I’m going to slow down my internet connection. The developer tools have an option for throttling an internet connection for testing purposes, and the developer tools switch over to the network panel. Next, there’s an option at the top for throttling the connection. By default, throttling is turned off. Let’s set the throttling option to the slowest speed available.
Uploading an Image
After doing so, I’m going to add the team members group block. Lastly, I’m going to upload an image. The image is going to take a long time to upload. While the image is uploading, let’s open the list view sidebar. I’m going to copy the team member block while the upload is occurring. Just because our upload is processing doesn’t mean our attributes have not changed.
Checking Attribute Values
Let’s check out the attribute values. For your convenience, I provided a simplified copy of the block. I’ve stripped most of the elements and data. I want to focus on the image URL attribute. Notice anything special about this attribute? It’s storing the blob URL. That’s not a good thing.
Understanding the Blob Issue
Here’s the thing about blobs. The URLs are pointing to a file in the user’s memory. This URL will not work on other browsers. In addition, blobs are temporary. If the same user who uploaded the file were to view this URL after opening and closing their browser, they would not be able to view the image. Our block should never store the blob URL. It should wait for the image to finish uploading.
Proposed Solution
There are different ways to solve this issue. I think the easiest way is to use state. We can store the URL in the component state. By doing so, we can store the URL without saving it with the attributes. After the upload is finished, we can store the URL in the attributes. Let’s tackle the solution together in your editor.
Step 1: Import useState
Open the index JS file for the team member block. At the top of the file, import the useState
function from the WordPress element package. Relax. Functions are available from this package. We do not need to import these functions from the official React package.
Step 2: Initialize State
Next, let’s scroll to the edit function. Inside this function, call the useState
function with the image URL attributes. The initial value of our state should be the image URL. After creating the state, let’s store the value and function in a variable called imagePreview
and setImagePreview
, respectively.
Step 3: Update Template
The last step is to update our template to use the state over the attribute. First, we are going to modify the condition for rendering the image. Change the condition from the image URL attribute to the image preview state. Do the same for the source attribute. Next, apply the same changes to the argument for the isBlobURL
function. Finally, in the media placeholder component, update the value for the disabled media buttons property to the image preview state.
Updating the onSelect Event
We’re almost finished. The on select event will be updated to the function. This function should update the state during an upload. First, let’s move the set attribute function to the else block. The attributes will only be updated after the upload is complete. If the URL is not a blob, we can safely update the attributes after the else block. Let’s call the setImagePreview
function with the new image URL variable.
Testing the Solution
By making these changes, we are not updating the attributes until a valid URL has been created. However, we still need to be able to preview the image for this case. We are storing the URL in the component state. Let’s test our log in the browser.
Step 1: Refresh and Upload
Refresh the page and add the team members group block. If you have throttling on, you may want to turn it off until the page has loaded. After the page has loaded, try uploading a new file.
Step 2: Copying the Block
Next, try copying the block to your clipboard. I created a copy of the block. Unlike last time, the blob URL is not stored in the block’s attributes. However, we’re still able to preview the image. That’s exactly what we wanted.
Clearing Blobs from Memory
Introduction
We are going to clear the blobs from memory. As mentioned before, blobs are stored in the client system memory after the image is finished uploading. The URL for the image swaps from the blob URL to an HTTP URL. However, just because the URL changes doesn’t mean the blob disappears.
Understanding Memory Leaks
There’s a concept known as a memory leak. A memory leak is when data is not released from a system. Browsers must allocate resources for files and variables. If the user navigates away from a page, these resources are released by freeing memory. New data can then be stored. Otherwise, a user’s system may become congested. This can lead to slow performance and sluggish behavior.
Memory Management in Browsers
Browsers do an incredible job of releasing memory. React also releases memory on our behalf. However, that doesn’t mean we can be lazy. We have a memory leak in our program. A blob will continue to persist. Blobs are released after the browser has been closed. However, the user may not close the browser after uploading a file; they may want to continue editing their site. Files can occupy large amounts of memory. It would be a good idea to release a blob after the image is uploaded.
Proposed Solution
WordPress has a function for releasing memory. Let’s try using this function to fix our memory leak issue.
Step 1: Import the Function
In your editor, open the index JS file for the team member block. From the WordPress blob package, import a function called revokeBlobURL
. This function will release memory for a blob URL.
Step 2: Update the onSelect Event
Scroll to the on select event from the media placeholder component. We are going to call this function at the end of the block. We can safely assume that this block is executed after an image has been uploaded. This function accepts the blob URL as an argument. Let’s pass in the image preview state, which contains the blob URL. Even if this variable does not contain a blob URL, WordPress will not complain. It’s perfectly safe to call it.
Testing the Solution
After making those changes, let’s test our code in the browser. Refresh the page. I’m going to upload an image with the console panel opened from the developer tools. As the image is uploaded, an error should not be thrown by the block. As long as an error is not thrown by the browser, we can consider our solution a success.
Refactoring the Team Member Block
Introduction
We are going to refactor the team member block. In my editor, I have the index JS file opened. This file is starting to get large and it’s going to continue to grow in future lectures. We are going to start adding code for modifying the team member image. In addition, the social media links must be functional to reduce bloat. I recommend separating the edit and save functions into separate files.
Why Refactor?
It’s common practice to create separate files for each function. You will see this practice implemented in various blocks. It’s a great way to refactor a block.
Step-by-Step Process
Step 1: Create New Files
Let’s give it a shot. Inside the directory for the team member block, create two files called Edit.js
and Save.js
.
Step 2: Move Functions
Next, let’s switch back to the index file. From this file, cut the edit function. Paste it into the edit file. Next, replace the name with the function keyword. Lastly, export the function under the default namespace.
Step 3: Repeat for Save Function
Back in the index file, grab the save function. We are going to do the same thing as before. Export this function from the save file.
Step 4: Move Import Statements
Both files won’t be functional without the proper import statements. Let’s start moving the import statements to their respective files in the index file. Grab the import statements for the block editor, i.e., block and element packages. Paste these import statements into the edit file. Next, paste these same import statements into the save file. The save file does not need the components blob and element packages. We can safely remove them.
Step 5: Clean Up the Index File
From the block editor package, remove the inspector controls and media placeholder components. After moving everything over, we can start importing these functions back into the index file. At the top of the file, import the edit and save functions from their respective files.
Step 6: Update Register Block Type
The last step is to add these functions to the register block type function.
Testing the Refactor
Hopefully, nothing is broken. Just to make sure, let’s try testing our block in the Gutenberg Editor. Try adding the team members block to the editor. The block is fully functional. Everything we’ve worked on still works. We can add an image or modify the bio.
Adding a Toolbar Option for Replacing an Image
Introduction
We are going to add a toolbar option for replacing an image. The toolbar is extendable for blocks. We’re not limited to the options created by WordPress. The Gutenberg Editor provides an option for adding custom buttons in the toolbar. Let’s add a button for changing the image of a team member.
Step 1: Open the Edit File
In your editor, open the Edit.js
file.
Step 2: Import Required Components
First, we need to import two components from the components package: import two components called BlockControls
and MediaReplaceFlow
.
Step 3: Add Block Controls Component
Next, let’s use these components in our block. At the top of the fragment element, add the BlockControls
component with opening and closing tags. The BlockControls
component will allow us to insert elements into the toolbar. This component must sit at the root level of the edit component. Anything written inside this component will be inserted into the toolbar. WordPress will handle positioning our content in the correct location inside this component.
Step 4: Add Media Replace Flow Component
Let’s add the MediaReplaceFlow
component. The MediaReplaceFlow
component was introduced for adding a button in the toolbar. This button is capable of opening the media library. It’s very similar to the media placeholder component. The difference is that the MediaReplaceFlow
component outputs a button. It does not output a complete interface for uploading a file or inserting a link. It’s just a simple button.
Step 5: Configure Button Properties
This component has a few properties. First, we can configure the text inside the button by adding the name
property. Let’s translate the text with the __
function. The button text will contain the following message: “Replace Image.”
Next, the media library can preselect an image. I think it would be a good idea to preselect the currently uploaded image. This way, users can modify the alt text without selecting the image again. We can preselect an image by adding two properties. The first property is called mediaId
. The value for this property must be the ID of the media file. As mentioned before, WordPress stores a record of a media file in a database. Every file uploaded to WordPress has a unique ID associated with it. We stored this ID as an attribute. Let’s set this property to the imageId
attribute.
The next property is called mediaURL
. The value for this property must be the URL to the image. Let’s set this property to the imageURL
attribute. The URL does not need to be the URL to the original image; the URL can be to any of the image sizes.
Step 6: Conditional Rendering
After adding these two properties, the image will be preselected. Before going further, this button should not appear if an image has not been uploaded. The purpose of this button is to replace the image, not set the initial image. Let’s conditionally render the MediaReplaceFlow
component above this component. Add the following expression: imagePreview &&
. Next, move the MediaReplaceFlow
component after the &&
operator.
Step 7: Limit File Types
Great. Let’s keep working on the MediaReplaceFlow
component. The next step is to limit the file selected by the component. This component supports the accept
and acceptedTypes
properties that are identical to the properties with the same name on the media placeholder component. Let’s copy these properties from the media placeholder component to the MediaReplaceFlow
component.
Step 8: Add Event Handlers
We are almost finished. The next properties are events. Similar to the media placeholder component, the MediaReplaceFlow
component has an event for errors and image selections. Let’s add the onError
event. For this event, we are going to pass in an arrow function to handle the event. This function is provided the error as an argument. Inside the function’s body, let’s log the error with the console.error
function.
Next, add the onSelect
event. The value for this event will be the same function passed into the media placeholder component. However, I don’t think it’s a good idea to have two copies of the same function. One of them changes, the other needs to reflect those changes. Oftentimes, developers will outsource the function into a variable. This practice keeps our codebase readable and maintainable.
Step 9: Refactor Event Handlers
Scroll on to the media placeholder component. Let’s grab the function from the onSelect
event. Next, above the return statement, create a variable called selectImage
. The value for this variable will be the function from earlier. Next, let’s set the onSelect
event from both components to this function. We are going to do the same for the onSelectURL
event. Grab the function for this event from the media placeholder component. Next, create a variable called selectImageURL
with this function as the value. Lastly, set the onSelectURL
event on the MediaReplaceFlow
component to this variable. Update the media placeholder component to use this variable.
Quick Fix for the Toolbar
Introduction
We’re going to add multiple toolbar buttons. The current solution for our toolbars will need to be updated by wrapping the <BlockControls>
component with the condition instead of wrapping just the <MediaReplaceFlow>
component.
Current Implementation
So change this:
jsxCopy code<BlockControls>
{
imgPreview &&
<MediaReplaceFlow
name={__("Replace Image", "udemy-plus")}
mediaId={imgID}
mediaURL={imgURL}
allowedTypes={["image"]}
accept={"image/*"}
onError={(error) => console.error(error)}
onSelect={selectImg}
onSelectURL={selectImgURL}
/>
}
</BlockControls>
Updated Implementation
To this:
jsxCopy code{imgPreview && (
<BlockControls>
<MediaReplaceFlow
name={__("Replace Image", "udemy-plus")}
mediaId={imgID}
mediaURL={imgURL}
allowedTypes={["image"]}
accept={"image/*"}
onError={(error) => console.error(error)}
onSelect={selectImg}
onSelectURL={selectImgURL}
/>
</BlockControls>
)}
Adding a Custom Toolbar Button for Removing an Image
Introduction
We are going to add a custom toolbar button for removing an image. So far we have added the media replace flow component to render a button in the toolbar. But what if we need to output a custom button? WordPress has you covered.
Steps to Implement
1. Open the Editor JS File
First, open the editor JS file.
2. Import Toolbar Button
From the components package, import a component called ToolbarButton
. This component will render a button for the toolbar. We can use this component to add custom behavior to our block.
3. Add Toolbar Button
Scroll to the BlockControls
component below the media replace flow component. Add the ToolbarButton
component with opening and closing tags. Inside the tags, add the double underscore function with the following translated text: “Remove Image”.
4. Implement On Click Event
The ToolbarButton
component supports the onClick
event for handling clicks. Add this event to the component with an arrow function for handling the event. Inside this function, we can reset the attributes with the setAttributes
function. The imageID
attribute will be set to zero, and the imageAlt
and imageURL
attributes can be reset to empty strings.
5. Update State
Next, update the state by calling the setImagePreview
function with an empty string. This function should be called from the selectImageURL
function. Ensure you have updated it from the previous lecture.
6. Group Toolbar Buttons
We have multiple buttons in the toolbar. The more buttons there are, the more difficult it can be to read the toolbar. For readability, separate the buttons by adding the group
property to the BlockControls
component. The value for this property must be set to "inline"
.
Testing
Try testing the block by adding the team members group block to the page. Next, select an image. After selecting an image, an option will appear in the toolbar to remove it. Click on this option to remove the image.
Toggling the Field for Modifying the Image’s Alt Attribute
Introduction
In this lecture, we are going to quickly toggle the field for modifying the image’s alt attributes. Let me show you what I mean in my browser.
Demonstration
I’m viewing the team members group block. On this block, I’ve uploaded an image. If we click on the block, a field for modifying the image’s alt attribute will appear. I’ve added this field for conveniently modifying the image alt attributes. Otherwise, users would need to open the media library to modify this value.
Conditional Visibility
On the other hand, this field should not appear if an image is not available. We should hide this field from the client. Try this as an exercise: toggle the appearance of the field in the sidebar. You should be able to do so without a problem. Pause the video and good luck.
Solution
Welcome back! If you were able to toggle the fields, that’s great. If not, that’s fine. Let’s go through the solution together in your editor.
1. Open the Edit JS File
Open the edit.js
file.
2. Locate the Text Area Control Component
Search for the TextAreaControl
component. You can find it inside the InspectorControls
component.
3. Add Conditional Rendering
Above this component, add the following expression:
javascriptCopy code{ imagePreview && !isBlobURL }
This expression checks two conditions:
- The first condition checks if the
imagePreview
state variable has a value. - The second condition checks for a blob URL.
We do not want to render this option unless the image has been successfully uploaded. Otherwise, it’s not worth showing the field.
4. Final Adjustments
Move the field after the second &&
operator. That’s it! The field will only appear if an image is selected.
Adding Image Shape Modification to the Team Member Block
Introduction
We are going to add one more feature for modifying the image of the team member block.
Demonstration
In my browser, I have added the team members group block to the page. When I select this block, the sidebar shows two options. The second option allows us to select an image shape.
Custom Clip Paths
To give you an idea of what this looks like, check out the resource section of this lecture for a link to a site called Clippy. This site provides a playground for creating custom clip paths with CSS, which can be applied to images to give them different shapes.
Predefined Classes
I have prepared the CSS file to contain classes for applying clip paths to the image. There are three classes for modifying the image’s shape. All we need to do is apply the correct class to the image. You can check out the main CSS file to find this code, and feel free to add additional clip paths if desired.
Understanding the Attribute
The dropdown for selecting an image shape is already functional in our block configuration file, utilizing the imageShape
attribute. This attribute manipulates the image shape attributes.
Global Settings
You may wonder why this attribute is applied to the container block instead of allowing individual blocks to modify this setting. The reasoning is that it makes sense for all team members to share the same image shape. Modifying each block individually would be tedious.
Using Block Context
Now, to communicate this data between parent and child components, we’ll use Block Context. This allows us to grab data from a parent block.
Updating the Block JSON
- Open the Block JSON File for the team members group block.
- Add a Provides Context Property at the bottom:jsonCopy code
"providesContext": { "udemy-plus/image-shape": "imageShape" }
Here,udemy-plus/image-shape
is a unique ID for the data, andimageShape
is the attribute we’re exposing.
Updating the Team Member Block
- Open the Block JSON File for the team member block.
- Add a Uses Context Property:jsonCopy code
"usesContext": [ "udemy-plus/image-shape" ]
Updating the Edit Component
Destructuring Context
- In the edit component, destructure the
context
object at the top of the function:javascriptCopy codeconst { "udemy-plus/image-shape": imageShape } = context;
Generating Image Class
- Generate a class for the image before the return statement:javascriptCopy code
const imageClass = `wp-image-${imageID} image-${imageShape}`;
Updating the Image Template
- Update the image template to include this class:javascriptCopy code
<img className={imageClass} src={imgURL} alt={imgAlt} />
Testing
Now, let’s test our blocks in the browser. Refresh the page and add images to each team member block. By default, the blocks will have the hexagon image shape. When modifying the shape from the team members group block, you’ll see the images change in unison.
Preparing Social Media Links for the Team Member Block
Introduction
In this lecture, we’re going to prepare the social media links for team members. Not all members will have accounts for the same social media platforms, so our block needs to be flexible and allow for various links.
Setting Up Default Attributes
Update Block JSON
- Open the Block JSON File for the team member block.
- Inside the
attributes
, find thesocialHandles
attribute. - Add a Default Property:jsonCopy code
"socialHandles": { "type": "array", "default": [ { "url": "https://facebook.com", "icon": "facebook" }, { "url": "https://instagram.com", "icon": "instagram" } ] }
Icons from Bootstrap
- For the icons, we’ll use Bootstrap Icons. You don’t need to use Bootstrap as a whole; the icons can be added separately.
- The icons for Facebook and Instagram will be
"facebook"
and"instagram"
, respectively.
Looping Through Social Handles
Update the Edit JS File
- Open the Edit JS File.
- Locate the Div Tag with the class
social-links
. - Inside this tag, we’ll loop through the
socialHandles
attribute using themap
function:javascriptCopy code<div className="social-links"> {socialHandles.map((handle, index) => ( <a key={index} href={handle.url}> <i className={`bi bi-${handle.icon}`}></i> </a> ))} </div>
Key Attribute
- Each anchor element should have a
key
attribute. Using the index is a common practice when the array order is not likely to change. - This helps React keep track of elements, preventing funky behavior during updates.
Rendering Icons
- Inside each anchor tag, we’ll render the icon using the Bootstrap Icons syntax:javascriptCopy code
<i className={`bi bi-${handle.icon}`}></i>
Testing the Code
- Refresh the Browser.
- Add the team members group block.
- Verify that each team member has two icons for Facebook and Instagram.
Adding a Button for New Social Media Links
Introduction
We will add a button that allows users to add new social media links for a team member. We’ll also enhance the button with a tooltip to improve usability.
Setting Up the Button and Tooltip
Open the Edit JS File
- Import Required Components: At the top of your
edit.js
file, import theTooltip
andIcon
components from the components package:javascriptCopy codeimport { Tooltip, Icon } from '@wordpress/components';
Add the Tooltip and Button
- Scroll to the Loop for Social Media Links: After the loop that renders existing social media links, add the
Tooltip
component:javascriptCopy code<div className="social-links"> {socialHandles.map((handle, index) => ( <a key={index} href={handle.url}> <i className={`bi bi-${handle.icon}`}></i> </a> ))} <Tooltip text={__('Add social media handle', 'udemy-plus')}> <a href="#" onClick={(event) => { event.preventDefault(); // Add logic to add a new social media link here }}> <Icon icon="plus" /> </a> </Tooltip> </div>
Add Functionality to the Button
- Implement the onClick Event: Inside the
onClick
handler, prevent the default action and update thesocialHandles
attribute:javascriptCopy codeonClick={(event) => { event.preventDefault(); setAttributes({ socialHandles: [ ...socialHandles, { url: '', icon: 'question' } // New social media link with empty URL and question icon ] }); }}
Testing the Code
- Refresh the Browser and add the team members group block to the page.
- Hover over the Plus Button:
- You should see the tooltip appear with the message “Add social media handle.”
- Click the Plus Button:
- A new social media link should be added to the list, featuring a question mark icon.
Toggling the Plus Button in the Team Member Block
Introduction
In this lecture, we will implement functionality to toggle the visibility of the plus button in the team member block based on whether the block is currently selected. This will help keep the UI clean and relevant, showing the plus button only when the user is actively editing a block.
Step-by-Step Implementation
Open the Edit JS File
- Locate the Components Function: In your
edit.js
file, find the function where you define your block’s edit component.
Destructure the isSelected
Property
- Destructure the Property: In the function’s parameters, destructure the
isSelected
property:javascriptCopy codeconst TeamMemberBlockEdit = ({ attributes, setAttributes, isSelected }) => { // ... other code };
Update the Tooltip Visibility
- Scroll to the Plus Button: Find the section where you have the tooltip and the plus button.
- Add Conditional Rendering: Wrap the
Tooltip
component with a conditional expression to toggle its appearance:javascriptCopy code{isSelected && ( <Tooltip text={__('Add social media handle', 'udemy-plus')}> <a href="#" onClick={(event) => { event.preventDefault(); setAttributes({ socialHandles: [ ...socialHandles, { url: '', icon: 'question' } // New link with question mark icon ] }); }}> <Icon icon="plus" /> </a> </Tooltip> )}
Testing the Code
- Refresh the Browser: Add the team members group block to your page.
- Select and Deselect the Block:
- When the block is selected, the plus button should appear.
- Clicking outside of the block should hide the plus button, reducing clutter.
Adding Input Fields for Modifying Social Media Links
Introduction
In this lecture, we will build on our previous work by adding input fields that allow users to modify the social media links for team members. We’ve already set up the functionality to track which link is active, so now we can focus on creating the input fields.
Step-by-Step Implementation
Step 1: Open the Edit JS File
Open your edit.js
file where you have implemented the active link tracking.
Step 2: Set Up the Form Structure
Below the div
containing the social links, we will render a form with input fields for editing the selected link’s URL and icon.
- Check Active Link Conditions: Inside the conditional expression that checks if the block is selected and if an active social link exists, let’s add the form structure:javascriptCopy code
{isSelected && activeSocialLink !== null && ( <div className="team-member-social-edit-container"> {/* Input fields will go here */} </div> )}
Step 3: Create Input Fields
Now, we need to create two input fields: one for the URL and one for the icon.
- Input for URL: Inside the
div
, add the following input element:javascriptCopy code<input type="text" value={socialHandles[activeSocialLink]?.url || ''} onChange={(e) => { const newHandles = [...socialHandles]; newHandles[activeSocialLink].url = e.target.value; setAttributes({ socialHandles: newHandles }); }} placeholder={__('Social Media URL', 'udemy-plus')} />
- Input for Icon: Similarly, add an input for the icon:javascriptCopy code
<input type="text" value={socialHandles[activeSocialLink]?.icon || ''} onChange={(e) => { const newHandles = [...socialHandles]; newHandles[activeSocialLink].icon = e.target.value; setAttributes({ socialHandles: newHandles }); }} placeholder={__('Social Media Icon', 'udemy-plus')} />
Step 4: Test the Implementation
- Refresh the Browser: Open your page where the team members group block is located.
- Select Links:
- Click on the social media links. The input fields should appear below the links.
- Modify the URL and icon fields. The values should update correctly in the block’s attributes.
Adding Form Functionality for Editing Social Media Links
Introduction
In this lecture, we’ll enhance our team member block by adding functionality to edit social media links. This includes modifying the URL and icon, as well as providing an option to remove links.
Step-by-Step Implementation
Step 1: Open the Edit JS File
Open your edit.js
file where you have set up the active link tracking and the form structure.
Step 2: Import Required Components
At the top of the file, import the necessary components from the WordPress components package:
javascriptCopy codeimport { TextControl, Button } from '@wordpress/components';
Step 3: Add Text Control for URL
Inside the div
with the class team-member-social-edit-container
, add a TextControl
component for modifying the URL:
javascriptCopy code<TextControl
label={__('Your URL', 'udemy-plus')}
value={socialHandles[activeSocialLink]?.url || ''}
onChange={(url) => {
const temporaryLink = { ...socialHandles[activeSocialLink], url };
const temporarySocial = [...socialHandles];
temporarySocial[activeSocialLink] = temporaryLink;
setAttributes({ socialHandles: temporarySocial });
}}
/>
Step 4: Add Text Control for Icon
Next, create another TextControl
for modifying the icon:
javascriptCopy code<TextControl
label={__('Icon', 'udemy-plus')}
value={socialHandles[activeSocialLink]?.icon || ''}
onChange={(icon) => {
const temporaryLink = { ...socialHandles[activeSocialLink], icon };
const temporarySocial = [...socialHandles];
temporarySocial[activeSocialLink] = temporaryLink;
setAttributes({ socialHandles: temporarySocial });
}}
/>
Step 5: Add Remove Button
Below the input fields, add a button to remove the link:
javascriptCopy code<Button
isDestructive
onClick={() => {
const temporaryCopy = [...socialHandles];
temporaryCopy.splice(activeSocialLink, 1);
setAttributes({ socialHandles: temporaryCopy });
setActiveSocialLink(null);
}}
>
{__('Remove', 'udemy-plus')}
</Button>
isDestructive
: This property makes the button red to indicate that it’s a dangerous action.
Step 6: Testing the Implementation
- Refresh the Page: Open your WordPress editor and refresh the page where the team members group block is displayed.
- Modify Links: Click on the social media links to activate the form. You should be able to change the URL and icon.
- Remove Links: Test the “Remove” button to ensure it deletes the selected link and hides the form.
Saving the Team Member Block in the Database
Introduction
In this lecture, we’ll finalize the team member block by ensuring that team member images and social media links are saved correctly in the database. This block supports client-side rendering, so we can save its content directly without server access.
Step-by-Step Implementation
Step 1: Open the Save JS File
Open your save.js
file where you will implement the saving logic for the images and social media links.
Step 2: Define the Image Class
Create a variable for the image class, incorporating the shape that will be passed from the parent block:
javascriptCopy codeconst imageClass = `wp-image image-${id} image-${attributes.imageShape}`;
Step 3: Update the Block Attributes
Next, open your block’s JSON file to add the imageShape
attribute. Add the following to the attributes object:
jsonCopy code"imageShape": {
"type": "string",
"default": "hexagon"
}
Step 4: Set the Image Shape in the Edit Function
In your edit.js
file, set the image shape attribute before returning the template:
javascriptCopy codesetAttributes({ imageShape: context.imageShape });
This ensures the latest shape is used when saving.
Step 5: Dstructuring the Image Shape in the Save Function
In your save.js
file, destructure the imageShape
attribute from the attributes object:
javascriptCopy codeconst { imageShape } = attributes;
Step 6: Render the Image
Update the image rendering logic. Check if the image exists before rendering:
javascriptCopy codereturn (
<div className="author-meta">
{imageURL && (
<img
src={imageURL}
alt={imageAlt}
className={`${imageClass} ${imageShape}`}
/>
)}
Step 7: Render Social Media Links
Next, search for the div tag with the class social-links
and loop through the socialHandles
attribute:
javascriptCopy code<div className="social-links">
{socialHandles.map((handle) => (
<a key={handle.url} href={handle.url} target="_blank" rel="noopener noreferrer">
<i className={`bi bi-${handle.icon}`} />
</a>
))}
</div>
This will create anchor elements for each social media link.
Step 8: Final Review
Make sure your save.js
file now includes the complete logic for saving both the image and social media links.
Optimizing Attributes for the Team Member Block
Introduction
We will optimize the attributes for the team member block to reduce the amount of data stored in the database. By retrieving certain attribute values directly from the rendered HTML, we can minimize the size of the database entry.
Step-by-Step Implementation
Step 1: Open the Block JSON File
Open your block.json
file to modify the attributes for the team member block.
Step 2: Update Attributes to Use HTML Source
- Name Attribute:
- Set the source to HTML and define the selector.
"name": { "type": "string", "source": "html", "selector": ".author-meta strong" }
- Title Attribute:
- Similarly, set the source and selector.
"title": { "type": "string", "source": "html", "selector": ".author-meta span" }
- Bio Attribute:
- Update the bio attribute using the HTML source.
"bio": { "type": "string", "source": "html", "selector": ".member-bio p" }
Step 3: ID and Image Attributes
- Image ID: Keep this attribute as is since it does not take up much space.
- Image Alt Attribute:
- Set the source to attributes to retrieve from the
alt
attribute.
"imageAlt": { "type": "string", "source": "attribute", "selector": "img", "attribute": "alt" }
- Set the source to attributes to retrieve from the
- Image URL Attribute:
- Use the same properties but for the
src
attribute.
"imageURL": { "type": "string", "source": "attribute", "selector": "img", "attribute": "src" }
- Use the same properties but for the
Step 4: Social Handles Attribute
- Set the social handles attribute to query, allowing us to retrieve multiple values from the rendered HTML.jsonCopy code
"socialHandles": { "type": "array", "source": "query", "selector": ".social-links a", "query": { "url": { "type": "string", "source": "attribute", "attribute": "href" }, "icon": { "type": "string", "source": "attribute", "attribute": "data-icon" } } }
Step 5: Add Data Attribute for Icons
- Open the
save.js
file and add thedata-icon
attribute to the anchor element for the social links.
javascriptCopy code<a key={handle.url} href={handle.url} target="_blank" rel="noopener noreferrer" data-icon={handle.icon}>
<i className={`bi bi-${handle.icon}`} />
</a>
Step 6: Testing the Changes
- Refresh your page and add the team member block.
- Make some modifications and save the block.
- Refresh the page again to ensure everything is working correctly.
Adding a Preview Feature to the Team Members Group Block
Introduction
We will enhance the team members group block by adding a dynamic preview feature. This will allow users to see a live representation of the block as they hover over it in the block editor.
Step-by-Step Implementation
Step 1: Open the Block JSON File
Open your block.json
file to modify the block’s configuration.
Step 2: Configure the Example Object
At the bottom of the JSON object, add an example
object to configure the preview.
jsonCopy code"example": {
"attributes": {
"columns": 2
},
"innerBlocks": [
{
"name": "udemy/team-member",
"attributes": {
"name": "John Doe",
"title": "CEO of Udemy",
"bio": "This is a short description of the team member.",
"imageURL": "https://example.com/image1.jpg"
}
},
{
"name": "udemy/team-member",
"attributes": {
"name": "Jane Doe",
"title": "CTO of Udemy",
"bio": "This is a short description of the team member.",
"imageURL": "https://example.com/image2.jpg"
}
}
]
}
Step 3: Update Inner Block Configuration
- In the
innerBlocks
array, you can add objects representing the team member blocks. - Make sure to replace
https://example.com/image1.jpg
andhttps://example.com/image2.jpg
with actual image URLs from a resource like Pick Some.
Step 4: Refresh the Gutenberg Editor
- Save your changes and refresh the Gutenberg editor.
- Search for the team members group block in the sidebar.
Step 5: Check the Preview
- Hover over the block to see the updated preview. The preview should now show the team members with the specified attributes.