-
Notifications
You must be signed in to change notification settings - Fork 39
fix(atomic): race condition in atomic-tab-manager attribute reading during initialization #6774
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
Conversation
… for expression property Co-authored-by: fbeaudoincoveo <[email protected]>
packages/atomic/src/components/search/atomic-tab-manager/atomic-tab-manager.spec.ts
Outdated
Show resolved
Hide resolved
packages/atomic/src/components/search/atomic-tab-manager/atomic-tab-manager.spec.ts
Show resolved
Hide resolved
packages/atomic/src/components/search/atomic-tab-manager/atomic-tab-manager.spec.ts
Outdated
Show resolved
Hide resolved
…defined expression to simulate race condition Co-authored-by: fbeaudoincoveo <[email protected]>
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.
Pull request overview
This PR fixes an intermittent race condition in atomic-tab-manager that causes a SchemaValidationError when initializing tabs. The issue occurs when the parent component reads child atomic-tab properties before Lit's async property descriptor setup completes, causing expression to return undefined instead of its default value ''. The fix adds nullish coalescing (??) as a defensive fallback, ensuring the expression is always defined regardless of initialization timing.
Key changes:
- Added
?? ''fallback when readingtabElement.expressioninatomic-tab-manager.ts - Added comprehensive unit test that explicitly simulates the race condition using
Object.definePropertyto forceundefinedreturn values
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
packages/atomic/src/components/search/atomic-tab-manager/atomic-tab-manager.ts |
Adds nullish coalescing operator to handle undefined expression property during race condition |
packages/atomic/src/components/search/atomic-tab-manager/atomic-tab-manager.spec.ts |
Adds test that forces expression property to return undefined and verifies empty string fallback |
The code changes look excellent! The fix is minimal, correct, and follows established patterns in the codebase. The test properly simulates the race condition and validates the fix. This is a well-executed solution to a difficult-to-reproduce timing issue.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…ributes Co-authored-by: fbeaudoincoveo <[email protected]>
packages/atomic/src/components/search/atomic-tab-manager/atomic-tab-manager.ts
Outdated
Show resolved
Hide resolved
Co-authored-by: fbeaudoincoveo <[email protected]>
Fix Race Condition in atomic-tab-manager
Problem
Users intermittently encounter a
SchemaValidationErrorwhen initializingatomic-tab-manager:Root Cause
Race condition between parent and child Lit component initialization.
When
atomic-tab-manager.initialize()executes synchronously (triggered by the@bindings()decorator's context callback), it reads childatomic-tabelements' properties:The issue is that Lit's
@propertydecorator defines reactive properties asynchronously during component upgrade. When the race condition occurs:initialize()is called synchronouslytabElement.expression,tabElement.name, andtabElement.labelreturnundefinedinstead of the attribute valuesbuildTab()validates withrequiredEmptyAllowedStringschema and throwsSolution
Use
getAttribute()instead of Lit property accessors for all attributes, following the pattern fromatomic-sort-dropdown.ts:Why this works:
getAttribute()is synchronous and doesn't depend on Lit's property initializationatomic-sort-dropdown.ts)Why fallback operators are necessary:
TypeScript compilation requirement:
getAttribute()returnsstring | null, but theTabInfointerface requiresstring(non-nullable). The fallback operators are needed for the code to compile.For
name(uses|| ''):!namevalidation check (line 94) which throws an error before reachingthis.tabs.push()atomic-tab'sValidatePropsControllerensures name is required and non-empty when the component connectsFor
label(uses|| ''):atomic-tab'sValidatePropsControllerwhich validates it's required and non-empty when the component connectsFor
expression(uses?? ''):getAttribute()returnsnull?? ''convertsnullto an empty string which the schema validator acceptsbuildTab()would throw becauserequiredEmptyAllowedStringrejectsnullWhy NOT use
ChildrenUpdateCompleteMixin(previously tried approach)The mixin overrides
getUpdateComplete()to wait for children's async update cycles. However:initialize()is called synchronously in the bindings callbackawaitsupdateCompletebefore reading child propertiesWhy NOT use
?? ''on properties alone (nullish coalescing on property access, another previously tried approach)If attributes are explicitly set in markup but properties aren't initialized yet:
tabElement.expression ?? ''would return''whenexpressionisundefinedTesting
atomic-sort-dropdown.tsuses the same approach)💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.