-
Notifications
You must be signed in to change notification settings - Fork 0
Closed as not planned
Closed as not planned
Copy link
Labels
enhancementNew feature or requestNew feature or request
Description
Goal
Rewrite crates/rafters-adapters/src/generator.rs to use the discovered element_tag, has_children, and self_closing from ComponentStructure instead of hardcoding <button>. Add knobs_config field to TransformedBlock for playground controls integration. Remove all hardcoded element behaviors.
Exact Implementation Requirements
Required Changes to TransformedBlock
// crates/rafters-adapters/src/traits.rs
#[derive(Debug, Clone)]
pub struct TransformedBlock {
pub web_component: String,
pub tag_name: String,
pub classes_used: Vec<String>,
pub attributes: Vec<String>,
// NEW:
/// JSON config for knobs controls (variant keys, size keys, boolean props).
/// Empty string when no knobs needed.
pub knobs_config: String,
}Required Changes to generate_web_component
// crates/rafters-adapters/src/generator.rs
/// Generate a Web Component class from the extracted component structure.
/// Uses the discovered element tag and children support -- no hardcoded elements.
pub fn generate_web_component(tag_name: &str, structure: &ComponentStructure) -> String
/// Generate JSON config for knobs controls from discovered component metadata.
pub fn generate_knobs_config(structure: &ComponentStructure) -> StringBehavior Requirements
- Use
structure.element_tagindocument.createElement(...)instead of hardcoded'button' - Rename internal
#buttonfield to#el - Remove
this.#button.type = 'button'-- only a<button>needs that, and the generator should not assume element type - If
structure.has_childrenis true: add a<slot>element inside#el - If
structure.has_childrenis false (self-closing elements like<input>): no slot, no child content - Remove all hardcoded
loadingattribute handling (the "Loading..." text,aria-busy) - Remove
this.#button.disabled = isDisabled-- usesetAttribute/removeAttribute('disabled', '')generically for any element disabledattribute handling: only add if"disabled"is instructure.observed_attributes- Apply classes from
baseClasses + variantClasses[variant] + sizeClasses[size](existing logic, keep) - Apply disabled classes only if
"disabled"is in observed attributes AND element hasdisabledattribute set generate_knobs_configproduces JSON:Where{ "variants": ["default", "destructive", "outline"], "sizes": ["sm", "default", "lg"], "booleans": ["disabled"] }variants= keys fromvariant_lookup,sizes= keys fromsize_lookup,booleans= observed attributes that are typically boolean (heuristic: name matches common boolean patterns likedisabled,loading,checked,readonly,required,hidden,open, or the prop was typed asbooleanin the interface)
What to Remove
this.#button = document.createElement('button')->this.#el = document.createElement('{element_tag}')this.#button.type = 'button'(hardcoded button behavior)loadingspecial case with "Loading..." spanaria-busyfor loading- All references to
#button->#el
Error Handling
- None specific -- generator always produces valid JS from valid
ComponentStructure
Acceptance Criteria
Functional Tests Required
#[cfg(test)]
mod tests {
use super::*;
use crate::react::ComponentStructure;
#[test]
fn generates_button_element() {
let structure = ComponentStructure {
element_tag: "button".to_string(),
has_children: true,
self_closing: false,
variant_lookup: vec![("primary".into(), "bg-primary".into())],
..Default::default()
};
let output = generate_web_component("my-button", &structure);
assert!(output.contains("createElement('button')"));
assert!(output.contains("slot")); // has children -> slot
assert!(!output.contains("Loading")); // no hardcoded loading
}
#[test]
fn generates_input_element_no_slot() {
let structure = ComponentStructure {
element_tag: "input".to_string(),
has_children: false,
self_closing: true,
variant_lookup: vec![("default".into(), "border".into())],
..Default::default()
};
let output = generate_web_component("my-input", &structure);
assert!(output.contains("createElement('input')"));
assert!(!output.contains("slot")); // no children -> no slot
}
#[test]
fn generates_span_element() {
let structure = ComponentStructure {
element_tag: "span".to_string(),
has_children: true,
self_closing: false,
variant_lookup: vec![("default".into(), "bg-primary".into())],
..Default::default()
};
let output = generate_web_component("my-badge", &structure);
assert!(output.contains("createElement('span')"));
assert!(output.contains("slot"));
}
#[test]
fn no_hardcoded_button_type() {
let structure = ComponentStructure {
element_tag: "div".to_string(),
has_children: true,
..Default::default()
};
let output = generate_web_component("my-card", &structure);
assert!(!output.contains(".type = 'button'"));
}
#[test]
fn generates_knobs_config() {
let structure = ComponentStructure {
variant_lookup: vec![
("primary".into(), "bg-primary".into()),
("secondary".into(), "bg-secondary".into()),
],
size_lookup: vec![
("sm".into(), "h-8".into()),
("lg".into(), "h-12".into()),
],
observed_attributes: vec!["variant".into(), "size".into(), "disabled".into()],
..Default::default()
};
let config = generate_knobs_config(&structure);
assert!(config.contains("primary"));
assert!(config.contains("secondary"));
assert!(config.contains("sm"));
assert!(config.contains("disabled"));
}
#[test]
fn uses_el_not_button() {
let structure = ComponentStructure {
element_tag: "button".to_string(),
..Default::default()
};
let output = generate_web_component("test-el", &structure);
assert!(output.contains("#el"));
assert!(!output.contains("#button"));
}
}Rust Requirements
- Must compile with
cargo clippy --all-targets -- -D warnings - Generated JS must use safe DOM APIs only (createElement, setAttribute, appendChild, etc.)
- No innerHTML in generated JS
- Keep existing
to_pascal_caseandescape_stringhelpers
What NOT to Include
- The
<rafters-knobs>Web Component itself (separate issue) - Template integration (separate issue)
- BlockMode changes (separate issue)
File Locations
- Implementation:
crates/rafters-adapters/src/generator.rs - Trait update:
crates/rafters-adapters/src/traits.rs(addknobs_configfield) - Tests: inline in
generator.rs
Integration Requirements
Dependencies
- refactor(docs-rs): Replace regex extraction in ReactAdapter with ast_extract #766 (
ComponentStructurechanges) serde_jsonfor knobs config serialization
Success Criteria
- All tests pass
- Zero hardcoded element types in generated JS
- No "Loading..." text anywhere
- No
#buttonreferences -- all#el - Slot only rendered when
has_childrenis true -
knobs_configJSON generated from discovered metadata -
cargo clippy -p rafters-adapters --all-targetsclean
This issue is complete when: generate_web_component() produces element-agnostic Web Components that render the discovered HTML element with conditional slot support, and generate_knobs_config() produces JSON from discovered metadata with zero hardcoded assumptions.
Context & References
- Depends on: refactor(docs-rs): Replace regex extraction in ReactAdapter with ast_extract #766 (ComponentStructure changes)
- Current hardcoded button:
generator.rs:129 - Current "Loading..." text:
generator.rs:141-145 - Current
#buttonfield:generator.rs:60
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request