-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Block API: Adding the anchor support (id) #2394
Conversation
Codecov Report
@@ Coverage Diff @@
## master #2394 +/- ##
==========================================
- Coverage 26.58% 26.47% -0.11%
==========================================
Files 158 158
Lines 4857 4911 +54
Branches 817 827 +10
==========================================
+ Hits 1291 1300 +9
- Misses 3013 3052 +39
- Partials 553 559 +6
Continue to review full report at Codecov.
|
Love this, I dig the implementation. I think the only thing we need to do is think carefully about this: — that is, we either need a good label for this, or some help text. Or both. "HTML Anchor"? We could perhaps use this pattern for italicized text below: @karmatosed any idea for help text? |
Perhaps this:
|
0f0e158
to
4f9ee64
Compare
/** | ||
* Internal constants | ||
*/ | ||
const ANCHOR_REGEX = /[^\w:.-]/g; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems that older specifications had restrictions, but as of HTML5 is not as strict:
https://www.w3.org/TR/2016/REC-html51-20161101/dom.html#the-id-attribute
Or is the hashbang behavior dependent on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we should only avoid spacing characters
return ( | ||
<div className={ classnames( 'blocks-base-control', className ) }> | ||
{ label && <label className="blocks-base-control__label" htmlFor={ id }>{ label }</label> } | ||
{ children } | ||
{ !! help && <p className="blocks-base-control__help">{ help }</p> } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need any accessibility semantics here? aria-describedby
?
Do we need this to be built into the controls themselves, or is it fine if they're just the next sibling in the DOM (i.e. <TextControl /><p>Help Text</p>
). Otherwise, we ought to be consistent in how all of the controls accept a help
prop.
blocks/api/serializer.js
Outdated
@@ -50,20 +50,28 @@ export function getSaveContent( blockType, attributes ) { | |||
} | |||
|
|||
// Adding a generic classname | |||
const addClassnameToElement = ( element ) => { | |||
if ( ! element || ! isObject( element ) || ! className ) { | |||
const addGenericAttributes = ( element ) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mtias I think you'd mentioned these sorts of attributes (class
, id
) being "advanced". Maybe a more appropriate adjective here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if @jasmussen has other thoughts on how to separate advanced features and make them less pronounced. It may be easier to collapse in place instead of at the footer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this, I think it looks good. I don't think it's a high priority, it seems like an optimization. But I'm a fan of the idea and the look.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we rename this method addAdvancedAttributes
?
@@ -29,6 +29,8 @@ registerBlockType( 'core/heading', { | |||
|
|||
className: false, | |||
|
|||
supportAnchor: true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trying to think of another way we might scale this. A similar pattern exists in core with post type supports and theme supports. It's a bit different in that case because those supports are always opt-in, which could be represented as an array. If we need some default-true supports, maybe it could still be defined as an object?
supports: {
className: false,
anchor: true,
},
And we could then have a utility method for getting block supports accounting for the default.
Are there other "supports" types we could see adding in the future?
Should className
be opt-in? Then we could have:
supports: [
'className',
'anchor',
],
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally think the block alignments should be part of these generic attributes. And I don't have a strong preference on how to declare support for these attributes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not terribly happy with the way className
works currently, and I'm curious if it might be improved by changing it to an opt-in. Relating to #2035, it's just not very obvious how this works, particularly when implementing a block with a string return value from save
. Changing this to an opt-in at least signals that the block implementer has some understanding of how it works and how they might need to implement the behavior on their own. Otherwise, they can fall into the trap of implementing a string return from save
, and users of their block will be presented with a non-functional class name field.
As it relates here, this would have the side-benefit of granting us a consistent pattern for supports. Either it's undeclared and unsupported, or it's declared and therefore supported. Further, this is consistent with how supports work with custom post types and themes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, this sounds like a good direction to me. I think @mtias wanted it to be opt-out at first?
Do you think I should update this PR or should we make this consistent separately?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was another related discussion previously, I think when className
was introduced, about the proliferation/scalability of properties on the block API. It may have been @BE-Webdesign that raised it? In any case, it seems wise to build a pattern around such "boolean" features (either block supports it or it doesn't). Even the prefix as it stands here is indicative of scaling trouble: supportAnchor
, eventually we might need supportFoo
, supportBar
.
I don't mean for it to cause trouble for this pull request specifically, but if it's something we feel we ought to address, I think it should either occur here or prior to this pull request being merged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mmm, one thing that come to my mind, is that the "className" attribute is not only to declare className support but it serves as a fallback className if it's a string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think when className was introduced, about the proliferation/scalability of properties on the block API. It may have been @BE-Webdesign that raised it?
Yeah that was me and it was around useOnce/useMany
. That property especially might cause problems as it could have an entirely different value in different contexts.
A supports object sounds like a reasonable way forward. Haven't checked out the branch at all to see how it works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mmm, one thing that come to my mind, is that the "className" attribute is not only to declare className support but it serves as a fallback className if it's a string.
What requirements is this feature serving? (Why do we need it?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was just an easy way to override the default generated className if a plugin author wanted to do so. But we could remove it for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was just an easy way to override the default generated className if a plugin author wanted to do so. But we could remove it for now.
Can't a block author do this themselves by assigning className: false
then assigning a class name of their choosing to the returned element?
How do you think we ought to handle blocks which return strings from |
Maybe we don't have to handle them, these are really specific cases where we probably don't need an id. I think the "anchor" this makes sense essentially for heading blocks (all sort of heading blocks). |
0e8b695
to
7bf7f7b
Compare
This is looking fine. @youknowriad @jasmussen do you think for headings we could pre-fill with a suggestion based on the heading title? |
I don't know why I feel like this is controversial... but something tells me it is. Perhaps the danger of duplicating IDs if you have the same subheading twice? On one hand I feel like it adds tremendous value to auto-id all the headings, it's one of my fav. features of github docs. But on the other hand it feels like we should ship this thing as is, and then explore prefilling in a separate PR. There's also an argumen to be made that we should show a clickable link when you hover over a heading, like on Github, and that if we do this it should probably only happen if you really want that heading to have a anchor. I.e. it may conflict with prefilling. |
Prefilling wouldn't necessarily add anything automatically, it would only suggest. |
Interesting. How would that work, though? If it's a placeholder, it's replaced when you start typing. The only pattern I can think of is something akin to browser autocomplete, but that seems very difficult to build. |
It could be like the post format "suggested" thing. |
Ah, clever. I like it. I still think it shouldn't hold this PR up, unless it's trivially easy to build. |
Sounds good to leave for later. It may be good to display a |
Where should we show this? |
@youknowriad below the help text, probably. (It could eventually have a "copy link" button.) |
7bf7f7b
to
c7d550f
Compare
@youknowriad yes, I'd be fine with just returning the |
I've added the permalink and a button but it probably needs some design :) |
We can pass the url through https://github.com/WordPress/gutenberg/blob/master/editor/utils/url.js#L36 |
@mtias I guess we could move this function to the |
This looks good to me. 🚢 |
This PR starts the work on #1734
The approach here is to add a new block API property
supportAnchor
(yeah, another property :(). A block can opt-int to the anchor support by settingsupportAnchor: true
. This will add an Anchor Text Input to the sidebar (similar to the className behaviour).The serializer will automatically sets the
id
attribute to the wrapper node of the block.Notes
I've used an opt-in approach (enabled on heading only for now), because the serializer can't automatically add an attribute to any random block. Blocks with no wrapper or with a
save
function returning a string can't declare support for the anchor.Not done yet:
Thoughs on the approach here?