Skip to content

Conversation

DAreRodz
Copy link
Contributor

@DAreRodz DAreRodz commented Nov 8, 2024

What?

The Interactivity API context can be nested, and inherited properties are supposed to be modified in the contexts where they are defined. This was not the case for properties inherited from contexts defined two or more levels above.

More info at #65623 (comment).

Fixes #65623.

This PR also fixes a bug in the Navigation block that seems related to the bug fix; see #66872 (comment).

How?

Applying the proposed fix mentioned in #65623 (comment).

Testing Instructions

You can follow the "Step-by-step reproduction instructions" at #65623 (comment) to reproduce the issue and check it disappears with the bug fix.

I've added unit tests to ensure the issue has been addressed and prevent it from happening again.

@DAreRodz DAreRodz added [Type] Bug An existing feature does not function as intended [Feature] Interactivity API API to add frontend interactivity to blocks. [Packages] Interactivity /packages/interactivity labels Nov 8, 2024
@DAreRodz DAreRodz self-assigned this Nov 8, 2024
@DAreRodz DAreRodz marked this pull request as ready for review November 8, 2024 13:45
@DAreRodz DAreRodz requested a review from luisherranz as a code owner November 8, 2024 13:45
Copy link

github-actions bot commented Nov 8, 2024

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: DAreRodz <darerodz@git.wordpress.org>
Co-authored-by: sirreal <jonsurrell@git.wordpress.org>
Co-authored-by: michalczaplinski <czapla@git.wordpress.org>
Co-authored-by: danielpost <danielpost@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Comment on lines +41 to +43
has: ( target, key ) =>
Reflect.has( target, key ) ||
Reflect.has( contextObjectToFallback.get( target ), key ),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const obj = key in target || ! ( key in fallback ) ? target : fallback;

And the problem arises when the key in fallback expression is evaluated.

I think the solution is to define a trap for the [[HasProperty]] internal method, returning true for inherited properties. We already have a trap for [[OwnPropertyKeys]] that returns the list of "own keys," which includes the inherited properties as well.
(from #65623 (comment))

This is confusing, but I think it makes sense. It does fix the issue in my testing. Are there tests already for the case where the value should be set on fallback?

With all the overrides to object behavior with proxies, it probably makes sense to add more special behaviors.

I was confused a bit since the has proxy changes the behavior of the in operator. It appears that it would change the behavior of the lines above, however those lines are on the unproxied objects so that usage of in is not impacted. Is that correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

those lines are on the unproxied objects so that usage of in is not impacted. Is that correct?

Correct. 🙂

Are there tests already for the case where the value should be set on fallback?

Yes, there are. We have a couple of unit tests checking prop modifications on fallback contexts:

describe( 'set', () => {
it( 'should modify props defined in it', () => {
const fallback: any = proxifyContext(
{ prop: 'fallback' },
{}
);
const context: any = proxifyContext(
{ prop: 'context' },
fallback
);
context.prop = 'modified';
expect( context.prop ).toBe( 'modified' );
expect( fallback.prop ).toBe( 'fallback' );
} );
it( 'should modify props inherited from its fallback', () => {
const fallback: any = proxifyContext(
{ prop: 'fallback' },
{}
);
const context: any = proxifyContext( {}, fallback );
context.prop = 'modified';
expect( context.prop ).toBe( 'modified' );
expect( fallback.prop ).toBe( 'modified' );
} );
it( 'should see changes in inherited props', () => {
const fallback: any = proxifyContext(
{ prop: 'fallback' },
{}
);
const context: any = proxifyContext( {}, fallback );
fallback.prop = 'modified';
expect( context.prop ).toBe( 'modified' );
expect( fallback.prop ).toBe( 'modified' );
} );
it( 'should create non-inherited props in itself', () => {
const fallback: any = proxifyContext( {}, {} );
const context: any = proxifyContext( {}, fallback );
context.prop = 'modified';
expect( context.prop ).toBe( 'modified' );
expect( fallback.prop ).toBeUndefined();
} );
it( 'should work with the proxified state', () => {
const state = proxifyState( 'test', { a: 1 } );
const fallback: any = proxifyContext( state, {} );
const context: any = proxifyContext( {}, fallback );
context.a = 2;
expect( context.a ).toBe( 2 );
expect( state.a ).toBe( 2 );
} );
it( "should modify props inherited from fallback's ancestors", () => {
const ancestor: any = proxifyContext(
{ ancestorProp: 'ancestor' },
{}
);
const fallback: any = proxifyContext(
{ fallbackProp: 'fallback' },
ancestor
);
const context: any = proxifyContext( {}, fallback );
context.ancestorProp = 'modified';
expect( context.ancestorProp ).toBe( 'modified' );
expect( fallback.ancestorProp ).toBe( 'modified' );
expect( ancestor.ancestorProp ).toBe( 'modified' );
} );
} );

@sirreal sirreal marked this pull request as ready for review November 8, 2024 16:10
@sirreal sirreal marked this pull request as draft November 8, 2024 16:11
@sirreal
Copy link
Member

sirreal commented Nov 8, 2024

Reset to draft, @DAreRodz is investigating the navigation block issue: #65623 (comment)

@sirreal sirreal self-requested a review November 8, 2024 16:12
@DAreRodz
Copy link
Contributor Author

Okay, this is interesting. The failing test passes without the fix, but the tested behavior (tab outside a nested submenu) doesn't work when the case is manually reproduced, both with and without the fix. 🤔

@DAreRodz
Copy link
Contributor Author

DAreRodz commented Nov 12, 2024

I've found it! It was the modal property inside submenu contexts. The property should be defined explicitly to prevent it from being inherited and thus manipulated in a parent context.

Note that although tests passed before without this bug fix, it seemed the tested behavior I mentioned above (using the tab key to move outside a nested submenu) was broken anyway.

In trunk (with tests passing) After 78c8b20
Screen.Recording.2024-11-12.at.14.36.31.mov
Screen.Recording.2024-11-12.at.14.38.14.mov

@DAreRodz DAreRodz marked this pull request as ready for review November 12, 2024 13:52
@DAreRodz DAreRodz requested a review from ajitbohra as a code owner November 12, 2024 13:52
@DAreRodz DAreRodz added the [Block] Submenu Affects the Submenu Block - for submenus in navigation label Nov 12, 2024
@DAreRodz DAreRodz merged commit d2bc9ea into trunk Nov 12, 2024
69 checks passed
@DAreRodz DAreRodz deleted the fix/iapi-context-inheritance-from-fallback-ancestor branch November 12, 2024 14:12
@github-actions github-actions bot added this to the Gutenberg 19.7 milestone Nov 12, 2024
@cbravobernal cbravobernal added the Backport to WP Minor Release Pull request that needs to be backported to a WordPress minor release label Nov 12, 2024
karthick-murugan pushed a commit to karthick-murugan/gutenberg that referenced this pull request Nov 13, 2024
…wo or more levels above (WordPress#66872)

* Add failing test

* Fix code

* Add changelog entry

* Define "modal" prop in submenu context


Co-authored-by: DAreRodz <darerodz@git.wordpress.org>
Co-authored-by: sirreal <jonsurrell@git.wordpress.org>
Co-authored-by: michalczaplinski <czapla@git.wordpress.org>
Co-authored-by: danielpost <danielpost@git.wordpress.org>
cbravobernal added a commit that referenced this pull request Nov 24, 2024
…wo or more levels above (#66872)

* Add failing test

* Fix code

* Add changelog entry

* Define "modal" prop in submenu context

Co-authored-by: DAreRodz <darerodz@git.wordpress.org>
Co-authored-by: sirreal <jonsurrell@git.wordpress.org>
Co-authored-by: michalczaplinski <czapla@git.wordpress.org>
Co-authored-by: danielpost <danielpost@git.wordpress.org>
cbravobernal added a commit that referenced this pull request Nov 26, 2024
* Interactivity API: fix property modification from inherited context two or more levels above (#66872)

* Add failing test

* Fix code

* Add changelog entry

* Define "modal" prop in submenu context

Co-authored-by: DAreRodz <darerodz@git.wordpress.org>
Co-authored-by: sirreal <jonsurrell@git.wordpress.org>
Co-authored-by: michalczaplinski <czapla@git.wordpress.org>
Co-authored-by: danielpost <danielpost@git.wordpress.org>

* Remove last ts refactor

---------

Co-authored-by: DAreRodz <darerodz@git.wordpress.org>
Co-authored-by: sirreal <jonsurrell@git.wordpress.org>
Co-authored-by: michalczaplinski <czapla@git.wordpress.org>
Co-authored-by: danielpost <danielpost@git.wordpress.org>
@t-hamano
Copy link
Contributor

Removing Backport to WP Minor Release label because #67259, which is equal to this PR, has already been merged into wp/6.7 branch.

@t-hamano t-hamano removed the Backport to WP Minor Release Pull request that needs to be backported to a WordPress minor release label Apr 23, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Block] Submenu Affects the Submenu Block - for submenus in navigation [Feature] Interactivity API API to add frontend interactivity to blocks. [Packages] Interactivity /packages/interactivity [Type] Bug An existing feature does not function as intended
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

Interactivity API: Context not updating properly from radio button click with nested contexts
4 participants