-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Annotation API #7718
Annotation API #7718
Conversation
d670f2b
to
e100b06
Compare
@iseulde - is there any way we could allow register custom TinyMCE plugins? It looks like this is the blocker for having annotations developed as a plugin. Another question is if we want to have it included in the core at some point. |
With the |
An alternative implementation of this PR can be found in the Whenever #7890 is merged I will make |
318435e
to
7aa0fa3
Compare
Could you write some docs as part of this PR that explain how to use the annotations API?
Could you elaborate? Why not? |
Because of the new
Will do. |
I'm adding this PR to 4.1 milestone to ensure it is included early in the process. As discussed during JS weekly chat yesterday, it depends on #10068 which @iseulde is going to work on as soon as 4.0-RC is out. Those two task depend on each other, so let's make sure we shape formatting API for RichText in a way which makes this PR work :) |
I'm moving it to 4.2 as we decided that 4.1 should be UI freeze focused. 4.2 is planned to be focused on API freeze and this PR fits perfectly to that description :) |
594fc27
to
64acd0c
Compare
} ); | ||
|
||
it( 'allows an annotation to be removed', () => { | ||
const state = annotations( { |
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.
A good enhancement to these tests would be to apply deepFreeze
on the original states. (also need a dev dependency)
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.
LGTM overall 👍 Thanks for your patience @atimmer
Anyone for a quick second PR as this is a big addition.
selector, | ||
}; | ||
|
||
if ( selector === 'range' ) { |
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.
Isn't the selector
argument redundant if you also have to provide a range
value for it to be used?
In other words, can't we infer selector === 'range'
by the presence of a truthy range
?
switch ( action.type ) { | ||
case 'ANNOTATION_ADD': | ||
const blockClientId = action.blockClientId; | ||
const newAnnotation = { |
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.
If the action was shaped such that the annotation itself was an object value, we wouldn't have to reconstruct what essentially amounts to const newAnnotation = omit( action, 'type' );
e.g. something like:
action = { type: 'ANNOTATION_ADD', annotation: { id, /* .. */ } };
...state.all, | ||
newAnnotation, | ||
], | ||
byBlockClientId: { |
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.
This is redundant data?
getAnnotationsByBlockClientId = ( state, blockClientId ) => filter( state.annotations.all, { blockClientId } );
@@ -839,25 +839,25 @@ export class RichText extends Component { | |||
} ).body.innerHTML; | |||
} | |||
|
|||
valueToFormat( { formats, text } ) { | |||
valueToFormat( value ) { |
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.
Why?
Where's the unit test which would have failed under the previous implementation?
|
||
const { name, ...settings } = annotation; | ||
|
||
registerFormatType( name, settings ); |
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 entirely related to this pull request, but in mind of #11611, there's an inconsistency we're considering in how / when formats become registered, particularly for "core" formats not specifically registered within @wordpress/format-library
.
*/ | ||
|
||
export function applyAnnotations( record, annotations = [] ) { | ||
annotations.forEach( ( annotation ) => { |
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.
This is a perfect use-case for Array#reduce
:
return annotations.reduce( ( recordResult, annotation ) => {
// ...
return applyFormat(
recordResult,
{ type: 'core/annotation', attributes: { className } },
start,
end
);
}, record );
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'd do the refactoring but there aren't any unit tests to verify the behavior so I don't feel confident.
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.
Blocking items resolved. Consider unresolved conversations for subsequent pull requests.
<3 |
Description
This PR includes an API to annotation text inside blocks. This PR on its own is mostly useful to plugin authors. This would make it possible for a feature plugin to build #3026. It also allows plugins like Jetpack or Yoast SEO to highlight certain parts of the text to make their feedback more contextual and actionable.
The best way to see this in action is combine it with this Yoast SEO PR: Yoast/wordpress-seo#10265
XPath has been chosen because it is a format that is compatible with the W3C annotation standard. This will give it future compatibility with front-end annotations. Partial XPaths can really easily be combined into a fully qualified XPath that works globally on a certain page. XPath is also the only format in the W3C annotation standard that can refer to text node. This is in contrast to the CSS selector which can't refer to text nodes.
All annotations in the editor get an
annotation-text
and anannotation-text-[source]
class to they can be styled by the source of the annotation.Browser compatibility
document.evaluate
is not supported in all browsers, but for this version of the PR I don't usedocument.evaluate
. I parse the XPath myself to result in a position within the data structure forrich-text
.Doesn't include, but should be added in a later iteration
RichText
an annotation is for. Currently this implementation only works for block with a singleRichText
.comment
,author
, etc.How has this been tested?
Screenshots
Related
Commenting API: #3026.
REST API for annotations (required for Commenting API): #4685
Checklist: