-
Notifications
You must be signed in to change notification settings - Fork 4.5k
Interactivity API: fix property modification from inherited context two or more levels above #66872
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: fix property modification from inherited context two or more levels above #66872
Conversation
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 If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.
To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
has: ( target, key ) => | ||
Reflect.has( target, key ) || | ||
Reflect.has( contextObjectToFallback.get( target ), key ), |
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.
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?
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.
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:
gutenberg/packages/interactivity/src/proxies/test/context-proxy.ts
Lines 77 to 158 in 67b7e04
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' ); | |
} ); | |
} ); |
Reset to draft, @DAreRodz is investigating the navigation block issue: #65623 (comment) |
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. 🤔 |
I've found it! It was the Note that although tests passed before without this bug fix, it seemed the tested behavior I mentioned above (using the
|
…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>
…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>
* 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>
Removing |
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.