WordPress / Learn

WordPress.org Learn - The canonical source for the code and content behind https://learn.WordPress.org
276 stars 101 forks source link

Course: Block Development #771

Closed westnz closed 9 months ago

westnz commented 2 years ago

Michael Burridge is working on this course and I will be supporting him along the way as Instructional Designer. Here is the initial planning doc: Block Development Course Objectives

mburridge commented 2 years ago

Thanks Wes.

azhiya commented 2 years ago

Michael Burridge is working on this course and I will be supporting him along the way as Instructional Designer. Here is the initial planning doc: Block Development Course Objectives

Hi, @westnz that link requires access. Can we make it read-only for public view?

westnz commented 2 years ago

Thank you @azhiya Michael changed the access to read-only for public view.

azhiya commented 2 years ago

Discussed in Slack: https://wordpress.slack.com/archives/C02RW657Q/p1654255617979579

westnz commented 2 years ago

COURSE UPDATE

Michael has started building the course in Sensei:

The outline is complete and he has drafted the Introduction and Module 1.

Herewith my feedback so far and recent discussions we have had:

Feedback – Introduction & Getting started module - Wes Thank you for the wonderful work you have started here. Herewith is some feedback from an educational point of view:

Introduction I absolutely love how you have laid out the learning journey in the introduction. Students exactly know what to expect, and what they will be learning, and creating. You highlight the progressive sequence of lessons clearly. My only suggestion is to use some bullet points when you mention “So, what will you be building in this course?” to break up the number of paragraphs and highlight it.

What is a block? Short and sweet. I think we need to look at how to add visually appealing features – here and throughout the course.

I would also suggest using the following blocks to highlight/summarize/emphasize certain points or concepts going forward: Info Callout, Tip Callout and Alert Callout. I noticed you have used the Tip Callout in the Build and run a project lesson. 😃

Set up your development environment Thorough list!

<should we give instructions for this? Docker is needed.> Yes, I think so <should we provide instructions on installing these?> Yes <should we cover using nvm to manage node/npm versions?> I think it is better to cover all our bases.

You mentioned; “Additionally you should know, or be prepared to learn, the basics of React.” Could you add links to more resources for those who are not well-acquainted with React?

Scaffold your first block The conversational style of writing and the way lessons have been broken into digestible pieces are effective.

I want to pause here and think about how we can engage learners throughout the course by using some techniques like using visuals, storytelling, examples of before and after, compelling dilemmas or asking interesting questions.

Understand the structure of the project You are really good at explaining things and structuring things in a logical and sequential order. Maybe we could use a Tip Callout to highlight the directories that will probably never be touched.

The video that we will be creating will be helpful here to go through the src directory. Adding too many images here will lead to cognitive overload.

Build and run your project This page flows very well visually for me. 👍️

Make some simple changes I assume this is still being worked on.

Michael's response:

Hey Wes, thanks so much for casting an eye over the course as it exists thus far, and for your in-depth comments.

Introduction

My only suggestion is to use some bullet points when you mention “So, what will you be building in this course?”

TBH I think I’d prefer the introduction to have a more conversational flow and a more engaging style.

What is a block?

I think we need to look at how to add visually appealing features

I would also suggest using the following blocks to highlight/summarize/emphasize certain points or concepts going forward

I agree with these points, I’ll revisit this (and probably many other lessons) later on when I’ve got the bulk of the content done to add refinements such as the ones you suggest here. For now I want to get a first draft done that can be revised and iterated on.

Set up your development environment

Could you add links to more resources for those who are not well-acquainted with React?

Done!

Regarding my questions, which you answered in the affirmative, my doubts are about overloading the learner with peripheral information that can be obtained elsewhere. Should we go for “completeness” or keep things tightly focussed on the subject matter we’re addressing?

Scaffold your first block

I want to pause here and think about how we can engage learners throughout the course by using some techniques like using visuals, storytelling, examples of before and after, compelling dilemmas or asking interesting questions.

Great points. Again something that can be addressed when I come to revise the content. For now I’m going to focus on getting as much content in as possible.

Understand the structure of the project

Maybe we could use a Tip Callout to highlight the directories that will probably never be touched.

Good idea. I’ll bear in mind ways in which I can make the content more visually appealing and more easily digestible once I’ve got the first draft down.

For now I’ve changed it to use bullet points just to break up the “monolithic block of text” look.

Make some simple changes I assume this is still being worked on.

Yup. 😄

Thanks again for giving it the once over Wes, and thanks too for your kind remarks. I’ll bear all your points in mind as I work on future lessons. I guess I’m more or less on the right track so for now I’ll just keep on keeping on.

Wes's feedback:

Absolutely! You are more than on the right track and it makes a lot of sense coming back to add refinements etc.

my doubts are about overloading the learner with peripheral information that can be obtained elsewhere. Should we go for “completeness” or keep things tightly focussed on the subject matter we’re addressing?

Good point. Maybe you could just add the info as a footer at the end of the lesson if you wish to. Something like this:

westnz commented 2 years ago

Update: Michael has now added content to module 2. He hopes to have the text-based part of the course finished by the end of August. 🤞

mburridge commented 2 years ago

Update: work done in the past two weeks (08 Aug 2022 - 19 Aug 2022)

I encountered a couple of minor bugs in the project as I reconstructed the project for the purposes of creating the course. They were mainly me making coding errors, but they still took a bit of time to track down.

The target for this period was to finish the four lessons that would complete the course up to the end of module 4. The course structure is continually being modified as the content develops and two new lessons were added. I therefore haven’t completed up to the end of module 4 as planned, so the target was not met in that sense, but I did complete four lessons.

The lesson "Conditionally display controls" has been moved to later in the course.

Achievements in this period

westnz commented 2 years ago

Thank you @mburridge. You sure have put a lot of work into this so far! Well done!!

westnz commented 2 years ago

Link to course (draft): https://learn.wordpress.org/course/introduction-to-block-development-build-your-first-gutenberg-block/

mburridge commented 2 years ago

The draft of the Block Development Course is now ready for review. :tada:

Please provide your feedback as a comment here by Thursday, 15 September.

Here's some things to take into account during your review of the course:

Thanks. I'm looking forward to your comments and feedback.

gziolo commented 2 years ago

Excellent work @mburridge. I started looking at the course to check how it all fits together and it's an impressive work. I like how you divided it into sections and how you introduce new concepts as the work in the block progresses.

I have some technical feedback to share for the first two modules:

General note, code examples don't follow WordPress Coding Standards. I'm not sure if that is something that is important here or not, but it's noticeable for someone who is forced to follow them on the daily basis.

What is a block?

The block editor, also known as Gutenberg, is the modern replacement for TinyMCE which is the editor that WordPress traditionally came with and with which you may already be familiar. Although it is an ongoing project still under active development, the block editor is already mature enough to be the default editor in new WordPress installations.

Technically speaking the block editor is the default WordPress editor from WordPress 5.0 (December 2018). Initially, the editor used TinyMCE internally and it was refactored later in the development process. I wouldn't highlight it being strictly a replacement for TinyMCE. It's far more important to emphasize instead the concept of blocks and how it lets users edit every aspect of the WordPress site using a single interface. It isn't posts and pages anymore.

Scaffold your first block

npx @wordpress/create-block multi-columns

Side note here. If you don't provide a name of the plugin/block then Create Block will prompt questions on the terminal and the user is able to customize many things. In effect, instead of manually editing block.json on "Build and run your project" page, they could pick those values when scaffolding the project. It's fine to show how to edit block.json the way it is, but it would be also nice to let folks the tool is more powerful.

Understand the structure of the project

save.js exports a function that determines the markup that will be saved to the post_content field in the wp_posts table when the post or page is saved.

It isn't only a post or page, it's all post types, including templates used by a block theme.

Add ‘Block Supports’ controls

I have mixed feelings when seeing experimental APIs like "__experimentalDefaultControls" in the official educational materials. In general, this is something that is meant to be used only in the Gutenberg plugin. See the recent post on that topic: https://make.wordpress.org/core/2022/08/10/proposal-stop-merging-experimental-apis-from-gutenberg-to-wordpress-core/.

It would be great to figure out how to make this API stable in the upcoming WordPress 6.1 release so everyone could freely use the stable version without this experimental prefix.

Define default settings in block.json

There are a number of components that are marked with __experimental... in Gutenberg,

While this is a great hint, it might feel very confusing for someone who is exposed for the first time to learning about building blocks for WordPress. In the ideal world, we would present how to use only stable APIs so people don't need to learn about all those nuances.

gziolo commented 2 years ago

Module 3, 4 & 5

Create custom inspector panel controls

It's a bit painful experience to avoid using useBlockProps and pass styles through style for features that are handled by the block editor. It's fine though as a learning exercise as it gets refactored later. What I'm worried about, it can lead also to subtle bugs if there is any style generated by useBlockProps because the style prop will always override it. I would feel more comfortable seeing this code always passing custom style and className through useBlockProps.

Add more configuration options

I have a similar concern as in the earlier module about using the experimental API that isn't considered ready for production use: __experimentalNumberControl component. Can we avoid using it or find a way to make it stable in WordPress 6.1 first?

Work with the InnerBlocks component

This is my favorite part when inner blocks get introduced :heart: 🎉

Refining the project

That's really nice to present all the opportunities to simplify the code 💯

Add style variations

I like the idea of showing how to use style variations. There is a drop cap native support in the block editor. It works with Paragraph blocks out of the box. You can look for dropCap in https://github.com/WordPress/gutenberg/blob/trunk/docs/how-to-guides/themes/theme-json.md. The limitation though is that you can't control the drop cap for inner blocks from the parent block.

The finished project

I don't see any experimental APIs included on this page. This is great, I'm wondering why there is a disconnect with the previous modules.

mburridge commented 2 years ago

Thanks so much for reviewing the course @gziolo and for your comments and feedback. Thanks also for your very kind remarks.

General note, code examples don't follow WordPress Coding Standards.

The repo for the project is here. Please feel free to submit PRs that bring the code into line with the WordPress Coding Standards (if you have the time) where you discern that it deviates from them and I'll update the tutorial accordingly.

I wouldn't highlight it being strictly a replacement for TinyMCE. It's far more important to emphasize instead the concept of blocks and how it lets users edit every aspect of the WordPress site using a single interface.

Good point. The earlier parts of the course are the ones most in need of revision I think, so I'll factor that in when I come to review and revise the course.

but it would be also nice to let folks the tool is more powerful.

Ideally I'd like to keep the course tightly focused around the project being built and not have too many side avenues to distract the learner. @jonathanbossenger has created a separate tutorial on Create Block which I reference in that lesson. Do you feel that it's important to explain the scope of the tool at that stage of the tutorial?

It isn't only a post or page, it's all post types, including templates used by a block theme

Another good point, I'll take it into account when reviewing and revising.

I have mixed feelings when seeing experimental APIs like "__experimentalDefaultControls"

I agree and have similar misgivings, and as you say this is confusing for the user. I've rewritten the following two lessons to remove all reference to "__experimentalDefaultControls":

Thanks once again for your review.

I see you've just posted another comment above 😄 - I'll address that separately.

gziolo commented 2 years ago

General note, code examples don't follow WordPress Coding Standards.

The repo for the project is here. Please feel free to submit PRs that bring the code into line with the WordPress Coding Standards (if you have the time) where you discern that it deviates from them and I'll update the tutorial accordingly.

In theory, you should be able to run the following commands with npm run in the repository:

https://github.com/mburridge/multi-columns/blob/d3833f2eee965f51026b957128aa456408f72c57/package.json#L10-L12

npm run format
npm run lint:css
npm run lint:js

It probably gets the default version of Prettier installed instead of the forked one for WordPress. I guess it's the issue with npm aliases... Yes, I checked package-lock.json file and it resolves to the official Prettier:

"prettier": {
      "version": "2.6.2",
      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz",
      "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==",
      "dev": true,
      "peer": true
    },

I guess it's what most people will experience so we have to live with this for now.

but it would be also nice to let folks the tool is more powerful.

Ideally I'd like to keep the course tightly focused around the project being built and not have too many side avenues to distract the learner. @jonathanbossenger has created a separate tutorial on Create Block which I reference in that lesson. Do you feel that it's important to explain the scope of the tool at that stage of the tutorial?

Fair enough 👍🏻

azhiya commented 2 years ago

Moving this to "Review in Progress" column.

mburridge commented 2 years ago

It's a bit painful experience to avoid using useBlockProps and pass styles through style for features that are handled by the block editor. It's fine though as a learning exercise as it gets refactored later. What I'm worried about, it can lead also to subtle bugs if there is any style generated by useBlockProps because the style prop will always override it. I would feel more comfortable seeing this code always passing custom style and className through useBlockProps.

I take your point, but as you observe this is a learning exercise. The presumed audience is developers new to React and maybe even new to JavaScript, having come from a PHP background. I'm trying in the course to introduce concepts gradually and in this instance show them a way to do it that they might already be familiar with, or at least able to understand the code, and then refactor it to show them a more "React way" of doing the same thing which might initially seem conceptually alien.

I don't know, I think this is a better way but I'm open to persuasion that your suggestion of introducing useBlockProps earlier in the course might be better for the learner.

There is a drop cap native support in the block editor. It works with Paragraph blocks out of the box.

Is that theme dependent? When using Twenty Twenty-Two the drop cap doesn't seem to be available. In any case it's a learning exercise and demonstrates how to use is-style-{slug}in the scss file. I couldn't think of another example for a style variation but am open to any ideas you might be able to offer in place of the Drop Cap style variation.

I don't see any experimental APIs included on this page. This is great, I'm wondering why there is a disconnect with the previous modules.

That's coz there aren't any! 😄 The example you mentioned in your earlier comment was a transitory thing which was replaced with a better version in the course. That has been removed now because, as you said, it was confusing for the learner and I now realise it was actually a bit redundant.

The other instance where an experimental component is mentioned is a box out in this lesson where it's referenced in case the learner has come across it elsewhere and they're discouraged from using it. Do you think it's better not to mention it at all?

Thanks for your kind comments on the other items, and thanks again for taking the time to review the course.

gziolo commented 2 years ago

It's a bit painful experience to avoid using useBlockProps and pass styles through style for features that are handled by the block editor. It's fine though as a learning exercise as it gets refactored later. What I'm worried about, it can lead also to subtle bugs if there is any style generated by useBlockProps because the style prop will always override it. I would feel more comfortable seeing this code always passing custom style and className through useBlockProps.

I take your point, but as you observe this is a learning exercise. The presumed audience is developers new to React and maybe even new to JavaScript, having come from a PHP background. I'm trying in the course to introduce concepts gradually and in this instance show them a way to do it that they might already be familiar with, or at least able to understand the code, and then refactor it to show them a more "React way" of doing the same thing which might initially seem conceptually alien.

I don't know, I think this is a better way but I'm open to persuasion that your suggestion of introducing useBlockProps earlier in the course might be better for the learner.

I had to check how useBlockProps is documented to have a better overview of why we have a different understanding how this should be explained. This is the code documentation:

https://github.com/WordPress/gutenberg/blob/35f14cc073e97a4c14c01bb5c28ac40944282964/packages/block-editor/src/components/block-list/use-block-props/index.js#L45-L52

This hook is used to lightly mark an element as a block element. The element should be the outermost element of a block. Call this hook and pass the returned props to the element to mark as a block. If you define a ref for the element, it is important to pass the ref to this hook, which the hook in turn will pass to the component through the props it returns. Optionally, you can also pass any other props through this hook, and they will be merged and returned.

This is a way to mark a HTML element as a block wrapper in the editor. It means that this is where all additional HTML attributes get injected to ensure that the block integrates seamlessly with all the UI elements like block toolbar, inspector controls, and related interactions.

The most important code looks as follows:

https://github.com/WordPress/gutenberg/blob/35f14cc073e97a4c14c01bb5c28ac40944282964/packages/block-editor/src/components/block-list/use-block-props/index.js#L145-L171

return {
        tabIndex: 0,
        ...wrapperProps,
        ...props,
        ref: mergedRefs,
        id: `block-${ clientId }${ htmlSuffix }`,
        role: 'document',
        'aria-label': blockLabel,
        'data-block': clientId,
        'data-type': name,
        'data-title': blockTitle,
        className: classnames(
            // The wp-block className is important for editor styles.
            classnames( 'block-editor-block-list__block', {
                'wp-block': ! isAligned,
                'has-block-overlay': hasOverlay,
            } ),
            className,
            props.className,
            wrapperProps.className,
            useBlockClassNames( clientId ),
            useBlockDefaultClassName( clientId ),
            useBlockCustomClassName( clientId ),
            useBlockMovingModeClassNames( clientId )
        ),
        style: { ...wrapperProps.style, ...props.style },
    };

There is a ton happening here, but what's most important is that you better pass all props through this function (yes, it's also React hook) to ensure that the block editor is able to merge everything together.

Now, let me explain a few issues with the code example included like:

    return (
        <>
            <RichText
                {...useBlockProps()}
                tagName="p"
                onChange={onChangeContent}
                value={attributes.content}
                placeholder={__("Enter some text here...")}
                style={{
                    columnCount: attributes.columnCount,
                    color: attributes.style.color.text,
                    background: attributes.style.color.background,
                }}
            />
        </>
    );

useBlockProps returns a long list of props that you can read from the code above including style. The code above also defines style. The way React works is that the last occurrence of the same prop wins, so any style provided from the block editor gets ignored. If you would also pass classname in the addition to that, then it would replace the long list of class names that the block editor needs to work correctly.

The other challenge is that useBlockProps is a React Hook so it needs always run and be in the same order. In effect, it's better to put it before the return statement. In effect, the way to minimize potential mistakes the following would work best in my opinion:

    const blockProps = useBlockProps({
        style={{
            columnCount: attributes.columnCount,
            color: attributes.style.color.text,
            background: attributes.style.color.background,
        }}      
    });
    return (
        <RichText
                {...blockProps}
                tagName="p"
                onChange={onChangeContent}
                value={attributes.content}
                placeholder={__("Enter some text here...")}
            />
    );

The developer experience is suboptimal here because it's hard to explain that className, style and ref have special handling here. It's a tricky one and super confusing, and the same pattern for passing props should apply to useBlockProps.save that is a regular function in save.

Aside: the following code would also work:

    const blockProps = useBlockProps({
        style={{
            columnCount: attributes.columnCount,
            color: attributes.style.color.text,
            background: attributes.style.color.background,
        }}
        tagName="p"
        onChange={onChangeContent}
        value={attributes.content}
        placeholder={__("Enter some text here...")}
    });
    return (
        <RichText {...blockProps} />
    );

I don't think it's used this way for core blocks. It's all about the mental mode where we mix block wrapper props with the props specific to the React element that we enhance. I wish we had a better story for it.

The interesting aspect here is that if we were to pass an id to the RichText component this way and with the previous way, then it would produce a different result. Here the block editor would override the id passed with the one from useBlockProps, but in the previous examples, it would use the id passed directly to RichText and that might have an unpredictable impact on how UI works.


There is a drop cap native support in the block editor. It works with Paragraph blocks out of the box.

Is that theme dependent? When using Twenty Twenty-Two the drop cap doesn't seem to be available. In any case it's a learning exercise and demonstrates how to use is-style-{slug}in the scss file. I couldn't think of another example for a style variation but am open to any ideas you might be able to offer in place of the Drop Cap style variation.

Yes, theme authors can disable certain features, including all other shared controls that are featured in the course. The same thing can happen to colors or padding when they get disabled. The other story is that block style variations need better integration with the features that themes can control with theme.json.


The other instance where an experimental component is mentioned is a box out in this lesson where it's referenced in case the learner has come across it elsewhere and they're discouraged from using it. Do you think it's better not to mention it at all?

I see what you did. You were faster than I was able to process the course 😄 I would keep an eye on that and double-check with folks new to the block development. As I mentioned before, it would be great to show only stable APIs in all educational material and hide experiments for internal usage in the Gutenberg plugin. I also can see how the code copied for the custom React component can work as a way to teach that you can build your controls, too.

jonathanbossenger commented 2 years ago

@mburridge I'm finally able to focus on reviewing the course. I will leave notes here on a per-module basis, in separate comments.

Introduction and Module 1

General comments: Great introduction and first module! I like how you keep it light, engaging, and focused on the learning outcomes. I'm excited to move through the rest of the course.

Specific comments

Introduction

Module 1

What is a block?

Understand the structure of the project

jonathanbossenger commented 2 years ago

Module 2

Make the block interactive

  1. On the section adding the destructured attributes to the Edit component's function

export default function Edit({attributes}) {

It's not clear that the block props are always passed to the Edit (and save) function, and that they contain the attributes object from the block.json (among other things). So a sentence or two explaining this might be helpful. I do this in my Attributes tutorial (which you can see here at about 2:25 on the video)

  1. In the section where you're adding the onChangeContent function
const onChangeContent = (val) => {
        setAttributes({ content: val });
    };

It's not clear where this should be in the Edit function, so it might be ideal to clarify that it needs to be before the return but inside the Edit function.

Add ‘Block Supports’ controls

  1. As far as I know, the Inspector Panel is referred to in the Block developer handbook as the Settings Sidebar while the component used to render items in that sidebar is called InspectorControls, so it might be a good idea to use the same name used in the docs.
  2. When adding color supports. and changing for example the background color, the .has-background class is applied to the p tag, which includes a default padding. It might be useful to mention that this is expected and that we'll customize that in the next step when adding spacing supports.

Define default settings in block.json

  1. Reminder to remove or update the Activity
westnz commented 2 years ago

Michael, let me just take this opportunity to congratulate you again on doing a superb job. It is no easy feat to create a course like this!!

Here is some feedback from my side:

Solid introduction!

Module 1 What is a block? There is still this note about 'fact check'

image

Understand the structure of your project Spend instead of spent

image

Build and run your project It might read better if we add a comma after Next? I have seen this in a few other places as well.

image

Change 'the' to 'then'

image

I would suggest breaking this sentence up into two parts

image

Create custom inspector panel controls Add a comma after However. There are a few other instances as well. Just a suggestion.

image

Heading and paragraph should be capital letters = Heading block / Paragraph block

image

Add more configuration options Chosen instead of chose

image

I would suggest breaking this sentence up into two parts:

image

Configuration options for the separator though should be through

image

I suggest breaking up this sentence into two parts

image

Add Style Variations Should it be previews or preview (singular)?

image

Conditionally display controls I assume truthiness is a commonly used word amongst coders? I had to google it :-)

image

Wrapping up Remember to remove these lessons ;-)

image

Quizzes: I assume these must still be created?

image
jonathanbossenger commented 2 years ago

Module 3

Create custom inspector panel controls

  1. Same note as earlier regarding Inspector Panel vs Settings Sidebar
  2. In the last lesson, the activity you asked the learner was to update the block to be wrapped in a div and not a p tag. Then when this lesson continues, the RichText component is still using a tagName of "p", so it might be a good idea to either tell the user to switch back to "p".
  3. Additionally, the last time you referred to the edit.js file, it just contained the RichText component. Then here, the RichText component is wrapped in <></>. I understand why this is, but the new learner might not. I also think this is a very important piece of React information, the fact that a React component can only have one top-level element. Also, it's probably only important to include this when the RangeControl component is used.
  4. This is the first time in this course that the code in the style.scss file is shown, so it might be confusing to the learner. It would either be useful to show what's in the style.scss file when you first reference it in module 1, or simply show what's currently in that file from create-block, before adding the column-count value.
  5. The course should probably guide the user on how much lorem ipsum text to add. For example, only using a sentence and setting column count to 4 only appears to render two of the columns. So it should probably specify something like "at least 2-4 sentences of text"
  6. I think what is needed in the section about background colors, is to explain the fact that because the RichText component is using useBlockProps, it's applying the styling from the style.scss file, but because the style object was defined in the block.json, this overrides anything set in the style.scss file. This explanation would also help with understanding why the styles from the file in item 2 above aren't already being rendered. This explanation will be helpful to developers, as there might be cases where they want to leave the styles in a stylesheet vs where they need to control that style element via block attributes, so this explanation is a great opportunity to explore the differences.
  7. I think that the section where you explain your reasoning behind the logical limits for the columnCount would be more impactful as a numbered list, instead of a series of paragraphs. A numbered list "breaks" the flow, and alerts the reader that something different/interesting is being presented.
  8. The last time the learner saw the imports from @wordpress/block-editor, they were all on one line, now you have them on new lines. Might be worth just mentioning both options work.

Add more configuration options

  1. In the section where the NumberControl is being created/imported, it might be useful to give a short (one or two sentence) explanation. While I agree that it's outside of the scope of this course, knowing what Number control is, and why you import it differently to WordPress components is useful to know.
  2. If at any point in time the user has chosen to save the post or page they have added the multi column block to, and they edit the edit.js and save.js code (to add the columnWidth or columnGap values), the block editor will display the "Attempt Block Recovery" UI (as the saved code and the save function will have changed). It might be useful to mention this in a callout.
jonathanbossenger commented 2 years ago

@mburridge Let me reiterate Wes' earlier comments, congratulations on doing a great job putting this course together. As I'm sure you know, creating a full course is no easy task, and I think you've done a wonderful job. I learned a bunch about block development.

Sadly, I was not able to complete a full review of the course in time, and I only managed to review up to module #3. However, I did not find any specific technical bugs, so I'm sure the rest of the course is technically correct.

As mentioned in an earlier comment, I've reviewed and left notes on a per module basis. Mostly what I've focused on where a) technical accuracy (did the code presented work for me) and b) new user flow (was I able to understand what was presented, step by step). I also tend to be very verbose in my feedback, so what you will probably see as walls of text could probably be trimmed down to one or two sentences.

I will add that nothing I have suggested is super critical, so you are welcome to ignore anything that I have added, I'm merely sharing what I would do differently if it was my course.

Again, congrats on the course content, I'm super excited to see it published! 🎉

mburridge commented 2 years ago

Sadly, I was not able to complete a full review of the course in time, and I only managed to review up to module https://github.com/WordPress/Learn/issues/3.

@jonathanbossenger thanks so much for reviewing up to this point and for your detailed feedback. Please feel free to continue reviewing as the time limit was imposed in order to provide some sort of deadline for community reviews. TBH I'm still open to receiving feedback from all sources, and I especially want the later stages of the course reviewed as the content gets increasingly more technical.

mburridge commented 2 years ago

@westnz thanks for your feedback. I'll make sure to address the points you raise.

jonathanbossenger commented 2 years ago

TBH I'm still open to receiving feedback from all sources, and I especially want the later stages of the course reviewed as the content gets increasingly more technical.

@mburridge ok thanks. I will keep it on my radar and do my best to review the rest of the content in the coming weeks.

@westnz as a side note, we might want to consider that reviewing a course takes considerably longer than reviewing a tutorial or lesson plan, and so we might need to allow more time for a course review.

We could still always publish the course, and ask any training faculty SMEs to review as/when they have time.

mburridge commented 2 years ago

Agreed, I'm not averse to extending the review period. In fact I expect the course to undergo changes and revisions even after publication as feedback comes in from people actually using the course to learn from describing where they're getting stuck or where explanations are inadequate or unclear.

westnz commented 2 years ago

Good idea @jonathanbossenger We could still always publish the course, and ask any training faculty SMEs to review as/when they have time.

mburridge commented 2 years ago

I'm going to have to rewrite part of the course. There's a bug in the code that I didn't pick up earlier, it's a "subtle bug" that @gziolo referred to above. He also goes into some detail as to why useBlockProps should be introduced earlier in this comment.

Although "subtle" the bug is pretty fatal for the course. To get around it I would either need to introduce code that later gets removed, which is messy and potentially confusing for the learner, or I need to introduce the useBlockProps hook earlier in the course, as Greg suggested.

I’m going to go down the latter route, but it’s going to entail some rewriting and restructuring of the course.

Note: To see the problem for yourself complete the project in the course up to the end of the lesson "Create custom inspector panel controls" in Module 3, then try changing both the text and background colour of the block.

mburridge commented 2 years ago

The good news is that I’ve finished re-structuring and re-writing sections of the course so it now not only circumvents the bug but is also now in accord with Greg’s suggestion. TBH I think it’s made the course better than it was.

This week I will be adding some activities/assignments/quizzes to the course with a view to getting a first version of the course published by the end of this week or early next week.

What would be great is if we can get a couple of people to run through the course and build the project from start to finish in order to test each step in sequence to check that each of the coding steps work as expected.

pbrocks commented 2 years ago

@mburridge By in accord with Greg's suggestion you mean you are not using useBlockProps()? I support strongly its use and am confused why it's difficult to explain how className comes about.

I'd like to review the course. Wondering if you are also open to stylistic comments regarding verbiage, or just focussing on the code at this point?

My background is in linguistics and I feel that the style of your written language is for native speakers and could be potentially simplified with some changes in word order. Also, in the first sentence where you are equating the Block Editor and Gutenberg is an oversimplification that is easily remedied by adding the first module of the ....

mburridge commented 2 years ago

By in accord with Greg's suggestion you mean you are not using useBlockProps()?

Hi @pbrocks, yes I am using useBlockProps and passing the styles through it. Initially I was introducing this concept later in the course but I have restructured the course and the project to introduce it earlier.

I'd like to review the course. Wondering if you are also open to stylistic comments regarding verbiage, or just focussing on the code at this point?

I'd love it if you would review the course. At the moment the main focus is on the code as we want to ensure that all the code samples in each step are accurate and that they all work correctly in order to expedite publication of the course.

That said stylistic feedback would, of course, also be very welcome. Please see here for other ideas for feedback that would be useful.

pbrocks commented 2 years ago

@mburridge I started something here and will continue, but thought I'd share now my initial thoughts.

mburridge commented 2 years ago

Thanks @pbrocks, end of the working day for me now. I'll check it out in more detail over the next couple of days.

mburridge commented 2 years ago

I've started adding quizzes and assignments to the course. So far all the lessons in Module 1 now have a quiz or an assignment.

@westnz it would be great to get 👀 on this to check that I'm on the right track with this kind of thing.

westnz commented 2 years ago

@mburridge You have done a great job so far creating quizzes that will allow learners to reflect on some of the key things they have learnt. You have also explained the correct answers to questions thoroughly.

My only suggestion is to add one or two practical questions where you show them an example or screenshot of code to fix or interpret. The aim is to retrieve knowledge, but also apply knowledge.

Just a reminder, you don't have to include a quiz after every lesson. The quizzes shouldn't feel overbearing to a learner. When you have included an activity such as this in the middle or at the end of a lesson, for example, it will suffice:

image

Question: Did you add 'an element' versus 'a element' on purpose?

image
juanmaguitar commented 2 years ago

These are some comments/suggestions from my review of the course:


From a technical point of view each block is in fact a React component. <fact check this!!!> You can implement a block from within a WordPress plugin, and a plugin is essentially what you will be building during this course.

I think the <fact check this!!!> at What is a block? should be removed


To scaffold your very first plugin that implements a block open up a terminal application and navigate to the directory where you want to create your project.

I think this phrase at Scaffold your first block could use a comma

To scaffold your very first plugin that implements a block, open up a terminal application and navigate to the directory where you want to create your project.


In the command above multi-columns is the user-defined part of the command. You can put whatever you want in its place. Whatever you put there will be the name of the project and the name of the project directory.

I would change this comment at Scaffold your first block for something like

The command above follows the structure npx @wordpress/create-block [options] [slug] being multi-columns the block slug used for its identification, the output location for scaffolded files, and the name of the WordPress plugin

mburridge commented 2 years ago

@westnz

My only suggestion is to add one or two practical questions where you show them an example or screenshot of code to fix or interpret. The aim is to retrieve knowledge, but also apply knowledge.

Great idea - I'll make sure to add some activities like that.

Question: Did you add 'an element' versus 'a element' on purpose?

Oh, that's annoying - that should read:

but it looks like you can't put the angle brackets in questions as it strips out HTML tags. I've fixed it as best I can but it's not ideal 😬

mburridge commented 2 years ago

@juanmaguitar

I think the <fact check this!!!> at What is a block? should be removed

TBH I'm still not sure if a block is in fact a React component or just constructed from React components. I think it's the former but not certain enough to commit to it in writing. I've rephrased the sentence completely.

I think this phrase at Scaffold your first block could use a comma

Hmm 🤔, I'm not totally convinced about that TBH.

I would change this comment at Scaffold your first block for something like

Great suggestion, thanks. I've rewritten that with an adapted version of your suggestion.

juanmaguitar commented 2 years ago

Some more comments/suggestions from my review of the course:


In the Scaffold your first block Quiz the question

You should run the ‘npx @wordpress/create-block’ command…

  • [ ] In the wp-content/plugins directory
  • [ ] In whichever directory you want to use to develop the project

Right Answer: In whichever directory you want to use to develop the project You would normally run the npx @wordpress/create-block command from within the wp-content/plugins directory of your WordPress installation, but you can create it elsewhere and symlink from the plugins directory if you wish.

I think it's confusing as in the content it seems to actually recommend doing the install from wp-content/plugins directory

To scaffold your very first plugin that implements a block open up a terminal application and navigate to the directory where you want to create your project. This will normally be in the wp-content/plugins directory of your WordPress installation, but you can create it elsewhere and symlink from the plugins directory if you wish.


In the Scaffold your first block Quiz the question

Which of these is NOT a file type generated by the create-block tool?

  • [ ] JavaScript
  • [ ] PHP
  • [ ] HTML
  • [ ] CSS

may be confusing as there's no specific explanation or comment about this in the lesson


In the Understand the structure of the project there's this text

First there are three directories:

  • The build directory is where the final deployable build of the block will go. You will probably never need to touch this directory directly.
  • The node_modules directory is where all the modules that the build process depends on live. Again you will in all likelihood never look in here.
  • The src directory is where you will spend most of your time. This is the directory that contains the files that you will work with to create the markup and the functionality of your block and which are ultimately compiled into the build directory. You’ll take a more detailed look at the contents of this directory later in this lesson.

I would try not to put the folders build, node_modules, and src at the same level and would try to focus more attention on the src folder

From this structure of files and folders, the src directory is where you will spend most of your time. This is the directory that contains the files that you will work with to create the markup and the functionality of your block and which are ultimately compiled into the build directory. You’ll take a more detailed look at the contents of this directory later in this lesson.

As for the rest of directories, you will probably never need to touch them directly:

  • The build directory is where the final deployable build of the block will go.
  • The node_modules directory is where all the modules that the build process depends on live.

In the Understand the structure of the project I would add some links to the following text

After the standard plugin header this file contains just a few lines of executable code consisting of a single function which is hooked onto the init action. This function calls the register_block_type() function passing the path to the previously mentioned build directory as a parameter. This is the location of block.json which contains the metadata that defines the block which the register_block_type() function needs.


You’re now going to add some interactivity to the block by making the static text editable by the user. But before you do that take a moment to examine edit.js.

I think this phrase at Make the block interactive could use a comma

You’re now going to add some interactivity to the block by making the static text editable by the user. But before you do that, take a moment to examine edit.js.


The first three lines are all import statements [...]

In this part of Make the block interactive I would specify more clearly the lines you're referring to

The first three lines are all import statements.

The first import statement (import { __ } from '@wordpress/i18n';) allows for text strings to be internationalized (you’ll look at this later in the course).

The third (import './editor.scss';) imports the editor.scss file that, as you saw earlier, defines the styling for the block in the editor.

The second import statement (import { useBlockProps } from '@wordpress/block-editor';) imports the useBlockProps hook, which is used in the only other code in the file apart from the import statements, namely the Edit() function. Here it’s used in the wrapper element (currently a <p> element) where, when used with the spread operator, it inserts attributes and classes, including attributes defined in the block.json file, although useBlockProps can also take an object defining attributes as a parameter.


You can see the attributes in the browser console. Add a console.log to the function and open the console in the browser before refreshing the page where you’re adding your new block. console.log(attributes); Make sure that changes are still being watched, via npm start, and that you save the changes to edit.js first!

This tip at Make the block interactive at this moment of the course generates the error ReferenceError: onChangeContent is not defined because the onChangeContent is still not defined

I would move it to after...

And now, because the handler function uses setAttributes to, erm…, set the attributes, that also needs to be destructured from the parameter passed to the Edit() function. `export default function Edit( { attributes, setAttributes } ) {'

ryanwelcher commented 2 years ago

@mburridge I just went through the course and built it step by step. Overall, it was really well-structured! I basically just copy/pasted each new code chunk I saw into my files and everything worked. There were a couple of instances where I got errors but that turned out to be because I didn't read the instructions and not because of the course.

The only notes I have are:

  1. I found that early in the course, I didn't see messaging around expecting the block to error when I made changes to the save.js. This was more prominent as the course went on so it might not be an issue but it might be worth adding the callouts to each step where save.js is modified.
  2. The Activity on Add more configuration options | Learn WordPress was confusing to me and there was no means to enter why it is incorrect if that was chosen.( was the answer no? ).

Overall, this is 🥇 ! Really excited to see it in the wild. Great work!

mburridge commented 2 years ago

Thanks @ryanwelcher.

  1. I've added a note on the error to this lesson
  2. I've rewritten the explanations that come up in the feedback box, hopefully making things clearer. I've also removed the question as to why it is incorrect.

@juanmaguitar thanks for the comprehensive feedback. I'll take a look at it in detail after the weekend.

juanmaguitar commented 2 years ago

All the lessons in Modules 1 – 4 now each have at least one quiz or activity

Reviews are ongoing and we are receiving great feedback. The course will be updated and amended in the light of comments and feedback as it's received.

With the addition of the interactive elements, we can consider the course as ready for publication (subject to ongoing reviews and modifications)

juanmaguitar commented 2 years ago

Some more comments/suggestions from my review of the course:


At Make the block interactive I would use some headings to make more clear the explanations over each one of the files. Something like...

block.json

Firstly, open block.json in your code editor a [...] [...] You’re done here. Save block.json and open edit.js to move onto step 2 of the “method”.

edit.js

[...]

westnz commented 2 years ago

Published: https://learn.wordpress.org/course/introduction-to-block-development-build-your-first-custom-block/

westnz commented 1 year ago

Hey @mburridge We have started asking for feedback from students who complete our courses to help improve our content. One of the questions is: "What can we add or change?".

Please validate feedback: https://github.com/WordPress/Learn/issues/1835

mburridge commented 1 year ago

Hi @westnz

Great that you're soliciting feedback. What do you mean by "Please validate feedback"? Do you want me to update the course with the points raised here?

westnz commented 1 year ago

Hey @mburridge

Yes, it is good to get direct feedback from students. If you agree with the feedback and feel it is valid, you could update the course with the points raised. Some feedback might not be valid, actionable, or within the scope of the course. Hope that makes sense.

courtneyr-dev commented 1 year ago

WP 6.4 https://github.com/WordPress/gutenberg/pull/52579 please review

jonathanbossenger commented 1 year ago

@courtneyr-dev do you have anything specific you think needs to be updated on this course due to the Interactivity API? As far as I remember, this course does not use either the File block or Navigation block, which is what the linked Gutenberg issue is about.