diff --git a/crates/html-validation/src/lib.rs b/crates/html-validation/src/lib.rs index 22bf4546..129cf4dc 100644 --- a/crates/html-validation/src/lib.rs +++ b/crates/html-validation/src/lib.rs @@ -27,6 +27,8 @@ #![deny(missing_docs)] pub use self_closing::is_self_closing; +pub use svg_namespace::is_svg_namespace; mod self_closing; +mod svg_namespace; diff --git a/crates/html-validation/src/self_closing.rs b/crates/html-validation/src/self_closing.rs index 3d39a458..cafcfe4b 100644 --- a/crates/html-validation/src/self_closing.rs +++ b/crates/html-validation/src/self_closing.rs @@ -1,6 +1,9 @@ use lazy_static::lazy_static; use std::collections::hash_set::HashSet; + +use super::svg_namespace::is_self_closing_svg_tag; + // Used to uniquely identify elements that contain closures so that the DomUpdater can // look them up by their unique id. // When the DomUpdater sees that the element no longer exists it will drop all of it's @@ -13,6 +16,7 @@ lazy_static! { .iter() .cloned() .collect(); + } /// Whether or not this tag is self closing @@ -26,4 +30,5 @@ lazy_static! { /// ``` pub fn is_self_closing(tag: &str) -> bool { SELF_CLOSING_TAGS.contains(tag) + || is_self_closing_svg_tag(tag) } diff --git a/crates/html-validation/src/svg_namespace.rs b/crates/html-validation/src/svg_namespace.rs new file mode 100644 index 00000000..9425ddf9 --- /dev/null +++ b/crates/html-validation/src/svg_namespace.rs @@ -0,0 +1,119 @@ +use lazy_static::lazy_static; +use std::collections::HashMap; + +lazy_static! { + // list of svg elements + // https://developer.mozilla.org/en-US/docs/Web/SVG/Element + // a hashmap of `(tag, is_self_closing)` + static ref SVG_NAMESPACED_TAGS: HashMap<&'static str, bool> = [ + // TODO: can cause conflict with html `a` + //("a", true), + ("animate", true), + ("animateMotion", false), + ("animateTransform", true), + ("circle", true), + ("clipPath",false), + // TODO: blocked with [issue](https://github.com/chinedufn/percy/issues/106) + //("color-profile",), + ("defs", false), + ("desc", false), + ("discard", true), + ("ellipse",true), + ("feBlend", true), + ("feColorMatrix", true), + ("feComponentTransfer", false), + ("feComposite", true), + ("feConvolveMatrix", true), + ("feDiffuseLighting", false), + ("feDisplacementMap", true), + ("feDistantLight", true), + ("feDropShadow", true), + ("feFlood", true), + ("feFuncA", true), + ("feFuncB", true), + ("feFuncG", true), + ("feFuncR", true), + ("feGaussianBlur", true), + ("feImage", true), + ("feMerge", false), + ("feMergeNode", true), + ("feMorphology", true), + ("feOffset", true), + ("fePointLight", true), + ("feSpecularLighting", false), + ("feSpotLight", true), + ("feTile", true), + ("feTurbulence", true), + ("filter", false), + ("foreignObject", false), + ("g",false), + ("hatch", false), + ("hatchpath", true), + ("image", true), + ("line", true), + ("linearGradient", false), + ("marker", false), + ("mask", false), + // TODO: undocumented + //("mesh",), + // TODO: undocumented + //("meshgradient",), + // TODO: undocumented + //("meshpatch",), + // TODO: undocumented + //("meshrow",), + ("metadata", false), + ("mpath", true), + ("path", true), + ("pattern", false), + ("polygon", true), + ("polyline", true), + ("radialGradient", false), + ("rect", true), + // TODO: can cause conflict with html `script` tag + //("script", false), + ("set", true), + ("solidcolor", true), + ("stop", true), + // TODO: can cause conflict with html `style` tag + //("style", false), + ("svg", false), + ("switch", false), + ("symbol", false), + ("text", false), + ("textPath", false), + // TODO: can cause conflict with html `title` tag + //("title", false), + ("tspan", false), + // TODO: undocumented + //("unknown",), + ("use", true), + ("view", true), + ] + .iter() + .cloned() + .collect(); +} +/// Whether or not this tag is part svg elements +/// ``` +/// use html_validation::is_svg_namespace; +/// +/// assert_eq!(is_svg_namespace("svg"), true); +/// +/// assert_eq!(is_svg_namespace("circle"), true); +/// +/// assert_eq!(is_svg_namespace("div"), false); +/// ``` +pub fn is_svg_namespace(tag: &str) ->bool { + SVG_NAMESPACED_TAGS.contains_key(tag) +} + +/// Whether or not this svg tag is self closing +pub(crate) fn is_self_closing_svg_tag(tag: &str) -> bool { + SVG_NAMESPACED_TAGS + .get(tag) + .map(|v|*v) + .unwrap_or(false) +} + + diff --git a/crates/virtual-dom-rs/tests/create_element.rs b/crates/virtual-dom-rs/tests/create_element.rs index a072ef80..010bda3d 100644 --- a/crates/virtual-dom-rs/tests/create_element.rs +++ b/crates/virtual-dom-rs/tests/create_element.rs @@ -27,6 +27,16 @@ fn nested_divs() { assert_eq!(&div.inner_html(), "
"); } +#[wasm_bindgen_test] +fn svg_element() { + let vdiv = html! {
+ +
}; + let div: Element = vdiv.create_dom_node().node.unchecked_into(); + + assert_eq!(&div.inner_html(), r#""#); +} + #[wasm_bindgen_test] fn div_with_attributes() { let vdiv = html! {
}; diff --git a/crates/virtual-node/src/lib.rs b/crates/virtual-node/src/lib.rs index 9fd54137..c0983f64 100644 --- a/crates/virtual-node/src/lib.rs +++ b/crates/virtual-node/src/lib.rs @@ -217,7 +217,11 @@ impl VElement { pub fn create_element_node(&self) -> CreatedNode { let document = web_sys::window().unwrap().document().unwrap(); - let element = document.create_element(&self.tag).unwrap(); + let element = if html_validation::is_svg_namespace(&self.tag){ + document.create_element_ns(Some("http://www.w3.org/2000/svg"), &self.tag).unwrap() + }else{ + document.create_element(&self.tag).unwrap() + }; let mut closures = HashMap::new(); self.attrs.iter().for_each(|(name, value)| { diff --git a/examples/isomorphic/app/src/views/home_view.rs b/examples/isomorphic/app/src/views/home_view.rs index 316009bd..59805286 100644 --- a/examples/isomorphic/app/src/views/home_view.rs +++ b/examples/isomorphic/app/src/views/home_view.rs @@ -39,7 +39,9 @@ impl View for HomeView { Click me!
In this time Ferris has made { click_count } new friends.
- + + + } }