-
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
Interactivity API: add wp-run
directive and useInit
& useWatch
hooks
#57805
Changes from 11 commits
cf96e62
3bbaff3
ec101b4
7018856
cc4b16f
9a32d11
61fee76
ef28d96
5823759
966f02d
3f34051
85b6948
3ec3afe
d104a08
56566cf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
"$schema": "https://schemas.wp.org/trunk/block.json", | ||
"apiVersion": 2, | ||
"name": "test/directive-run", | ||
"title": "E2E Interactivity tests - directive run", | ||
"category": "text", | ||
"icon": "heart", | ||
"description": "", | ||
"supports": { | ||
"interactivity": true | ||
}, | ||
"textdomain": "e2e-interactivity", | ||
"viewScript": "directive-run-view", | ||
"render": "file:./render.php" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
<?php | ||
/** | ||
* HTML for testing the directive `data-wp-run`. | ||
* | ||
* @package gutenberg-test-interactive-blocks | ||
*/ | ||
|
||
gutenberg_enqueue_module( 'directive-run-view' ); | ||
?> | ||
|
||
<div | ||
data-wp-interactive='{ "namespace": "directive-run" }' | ||
data-wp-navigation-id='test-directive-run' | ||
> | ||
<div data-testid="hydrated" data-wp-text="state.isHydrated"></div> | ||
<div data-testid="mounted" data-wp-text="state.isMounted"></div> | ||
<div data-testid="renderCount" data-wp-text="state.renderCount"></div> | ||
<div data-testid="navigated">no</div> | ||
|
||
<div | ||
data-wp-run--hydrated="callbacks.updateIsHydrated" | ||
data-wp-run--renderCount="callbacks.updateRenderCount" | ||
data-wp-text="state.clickCount" | ||
></div> | ||
</div> | ||
|
||
<div data-wp-interactive='{ "namespace": "directive-run" }' > | ||
<button data-testid="toggle" data-wp-on--click="actions.toggle"> | ||
Toggle | ||
</button> | ||
|
||
<button data-testid="increment" data-wp-on--click="actions.increment"> | ||
Increment | ||
</button> | ||
|
||
<button data-testid="navigate" data-wp-on--click="actions.navigate"> | ||
Navigate | ||
</button> | ||
|
||
<!-- Hook execution results are stored in this element as attributes. --> | ||
<div | ||
data-testid="wp-run hooks results" | ||
data-wp-show-children="state.isOpen" | ||
data-init="" | ||
data-watch="" | ||
> | ||
<div | ||
data-wp-run--mounted="callbacks.updateIsMounted" | ||
data-wp-run--hooks="runs.useHooks" | ||
> | ||
Element with wp-run using hooks | ||
</div> | ||
</div> | ||
</div> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { | ||
store, | ||
directive, | ||
navigate, | ||
useInit, | ||
useWatch, | ||
cloneElement, | ||
getElement, | ||
} from '@wordpress/interactivity'; | ||
|
||
// Custom directive to show hide the content elements in which it is placed. | ||
directive( | ||
'show-children', | ||
( { directives: { 'show-children': showChildren }, element, evaluate } ) => { | ||
const entry = showChildren.find( | ||
( { suffix } ) => suffix === 'default' | ||
); | ||
return evaluate( entry ) | ||
? element | ||
: cloneElement( element, { children: null } ); | ||
}, | ||
{ priority: 9 } | ||
); | ||
|
||
const html = ` | ||
<div | ||
data-wp-interactive='{ "namespace": "directive-run" }' | ||
data-wp-navigation-id='test-directive-run' | ||
> | ||
<div data-testid="hydrated" data-wp-text="state.isHydrated"></div> | ||
<div data-testid="mounted" data-wp-text="state.isMounted"></div> | ||
<div data-testid="renderCount" data-wp-text="state.renderCount"></div> | ||
<div data-testid="navigated">yes</div> | ||
|
||
<div | ||
data-wp-run--hydrated="callbacks.updateIsHydrated" | ||
data-wp-run--renderCount="callbacks.updateRenderCount" | ||
data-wp-text="state.clickCount" | ||
></div> | ||
</div> | ||
`; | ||
|
||
const { state } = store( 'directive-run', { | ||
state: { | ||
isOpen: false, | ||
isHydrated: 'no', | ||
isMounted: 'no', | ||
renderCount: 0, | ||
clickCount: 0 | ||
}, | ||
actions: { | ||
toggle() { | ||
state.isOpen = ! state.isOpen; | ||
}, | ||
increment() { | ||
state.clickCount = state.clickCount + 1; | ||
}, | ||
navigate() { | ||
navigate( window.location, { | ||
force: true, | ||
html, | ||
} ); | ||
}, | ||
}, | ||
callbacks: { | ||
updateIsHydrated() { | ||
setTimeout( () => ( state.isHydrated = 'yes' ) ); | ||
}, | ||
updateIsMounted() { | ||
setTimeout( () => ( state.isMounted = 'yes' ) ); | ||
}, | ||
updateRenderCount() { | ||
setTimeout( () => ( state.renderCount = state.renderCount + 1 ) ); | ||
}, | ||
}, | ||
runs: { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was first inside Again, this is my subjective impression, so if you think it's better to move them back to (Everything is a callback, isn't it? 🤷) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I think that we picked There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 3ec3afe. |
||
useHooks() { | ||
// Runs only on first render. | ||
useInit( () => { | ||
const { ref } = getElement(); | ||
ref | ||
.closest( '[data-testid="wp-run hooks results"]') | ||
.setAttribute( 'data-init', 'initialized' ); | ||
return () => { | ||
ref | ||
.closest( '[data-testid="wp-run hooks results"]') | ||
.setAttribute( 'data-init', 'cleaned up' ); | ||
}; | ||
} ); | ||
|
||
// Runs whenever a signal consumed inside updates its value. Also | ||
// executes for the first render. | ||
useWatch( () => { | ||
const { ref } = getElement(); | ||
const { clickCount } = state; | ||
ref | ||
.closest( '[data-testid="wp-run hooks results"]') | ||
.setAttribute( 'data-watch', clickCount ); | ||
return () => { | ||
ref | ||
.closest( '[data-testid="wp-run hooks results"]') | ||
.setAttribute( 'data-watch', 'cleaned up' ); | ||
}; | ||
} ); | ||
} | ||
} | ||
} ); |
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.
What's with the
setTimeout
s 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.
I know this is weird. I saw an infinite loop issue, at least with
state.renderCount
(line 76), so I eventually decided to usesetTimeout
for all callbacks.PS: I don't know, but regarding this comment, maybe these
callbacks
should have beenruns
(or the other way around 🤔).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.
Shouldn't we review that infinite loop issue?
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.
Any luck with 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.
Oh, wait. Isn't this just a "don't use
setState
inside the body function" kind of infinite loop?You can't do this:
You have to do this, which is equivalent to the
setTimeout
: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 haven't had a chance to investigate yet, but probably it is what you mention. I'll check tomorrow. 😄