feat(fast-html): add AttributeMap class for automatic @attr definitions#7354
Open
feat(fast-html): add AttributeMap class for automatic @attr definitions#7354
Conversation
AttributeMap inspects the JSON schema generated by TemplateElement for a custom element and defines @attr properties on the class prototype for all leaf-level bindings (simple {{foo}} and attribute {{bar}} paths that have no nested properties, no type, and no anyOf). - Add AttributeMap class in packages/fast-html/src/components/attribute-map.ts - Reads root properties from the Schema - Skips properties with nested 'properties', 'type', or 'anyOf' (not leaves) - Skips properties that already have an @attr or @observable accessor - Converts camelCase property names to dash-case (fooBar -> foo-bar) - Creates AttributeDefinition instances via Observable.defineProperty - Updates FASTElementDefinition.attributeLookup and propertyLookup - Integrate AttributeMap into TemplateElement (template.ts / index.ts) - Add AttributeMapOption constant and type - Add attributeMap option to ElementOptions interface - TemplateElement.options() stores attributeMap option - connectedCallback instantiates AttributeMap when attributeMap === 'all' - defineProperties() called after schema is fully populated - Add tests in attribute-map.spec.ts (browser E2E tests) - Add fixture in test/fixtures/attribute-map/ Usage: TemplateElement.options({ 'my-element': { attributeMap: 'all' }, }); Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…emplate updates Update AttributeMap.defineProperties() to also push each newly created attribute name to the existing observedAttributes array on the class. For all f-template-registered elements, registry.define() (which causes the browser to cache observedAttributes) is called AFTER defineProperties() runs, because composeAsync() waits for definition.template to be set before resolving. This creates a reliable window to mutate the array so the browser observes the dynamically-added attributes. Update tests to use element.setAttribute() inside page.evaluate instead of button clicks, testing both directions: - setAttribute() → attributeChangedCallback() → property → template re-render - property assignment → attribute reflection via DOM Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…om attribute-map fixture Tests no longer need window.Observable or window.__FAST__ to verify AttributeMap behaviour. Use Object.getOwnPropertyDescriptor to check that accessor get/set was added to the prototype, and verify attribute lookup via setAttribute behaviour instead of inspecting internal registry state. Also update the beachball change type to prerelease. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…by AttributeMap Add an AttributeMapWithExistingAttrElement fixture element with a pre-defined @attr foo property (default value 'original'). After f-template processes with attributeMap: 'all', tests confirm that: - the @attr default value is preserved (accessor was not re-defined) - setAttribute() still routes through the original @attr definition Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pull Request
📖 Description
This PR adds an
AttributeMapclass to@microsoft/fast-htmlthat automatically defines@attrproperties on a custom element's class prototype based on the JSON schema generated byTemplateElement.When
attributeMap: "all"is configured for an element viaTemplateElement.options(), theAttributeMapinspects the schema after template processing and creates@attr-style reactive properties for all leaf bindings — i.e. simple template expressions like{{foo}}or<div id="{{bar}}">that have no nested properties, no array type, and no child element references.Key behaviours:
fooBaris registered with attribute namefoo-bar{{user.name}}result inuserhaving sub-propertiesin the schema and are excluded@attror@observableare left untouched to avoid duplicating accessorsFASTElementDefinition:attributeLookupandpropertyLookupare patched soattributeChangedCallbackcorrectly delegates to the newAttributeDefinitionObservable.definePropertywith anAttributeDefinitioninstance directly, bypassing the decorator metadata path (which has already run before the template is processed)Usage
This mirrors the existing
ObserverMapintegration pattern.📑 Test Plan
13 Playwright tests were added across two spec files:
packages/fast-html/src/components/attribute-map.spec.ts— verifies accessor registration, camelCase→dash-case conversion, event handler exclusion, andFASTElementDefinitionlookup updates by navigating to the fixture pagepackages/fast-html/test/fixtures/attribute-map/attribute-map.spec.ts— end-to-end tests verifying template re-rendering when properties are set via JavaScriptAll existing tests (666 Playwright + 4 rules) continue to pass.
✅ Checklist
General
$ npm run change⏭ Next Steps
attributeMapsupport to the package README / documentation once the API stabilisesobservedAttributescan be updated after element registration to fully support the DOM attribute → property direction (currently, only property → DOM attribute reflection works for elements registered before the template is processed)