Skip to content

Commit 5cb7c95

Browse files
authored
Add logic to properly handle Phlex::SVG (#139)
* Add logic to properly handle `Phlex::SVG` * Handle case-sensitive SVG tags * RuboCop
1 parent c900251 commit 5cb7c95

File tree

4 files changed

+158
-8
lines changed

4 files changed

+158
-8
lines changed

gem/lib/phlexing/helpers.rb

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ module Helpers
99
Phlex::HTML::VoidElements.registered_elements.values +
1010
Phlex::HTML::StandardElements.registered_elements.values
1111

12+
SVG_ELEMENTS = Phlex::SVG::StandardElements.registered_elements.values.to_h { |element| [element.downcase, element] }
13+
1214
def whitespace
1315
options.whitespace? ? "whitespace\n" : ""
1416
end
@@ -59,17 +61,27 @@ def unwrap_erb(source)
5961
end
6062

6163
def tag_name(node)
62-
return "template_tag" if node.name == "template-tag"
63-
6464
name = node.name.tr("-", "_")
6565

66-
@converter.custom_elements << name unless KNOWN_ELEMENTS.include?(name)
66+
return name if name == "template_tag"
67+
return name if name.start_with?("s.")
68+
return name if KNOWN_ELEMENTS.include?(name)
69+
70+
@converter.custom_elements << name
6771

6872
name
6973
end
7074

71-
def block
75+
def block(params = nil)
7276
out << " {"
77+
78+
if params
79+
out << " "
80+
out << "|"
81+
out << params
82+
out << "|"
83+
end
84+
7385
yield
7486
out << " }"
7587
end

gem/lib/phlexing/options.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@
22

33
module Phlexing
44
class Options
5-
attr_accessor :component, :component_name, :parent_component, :whitespace
5+
attr_accessor :component, :component_name, :parent_component, :whitespace, :svg_param
66

77
alias_method :whitespace?, :whitespace
88
alias_method :component?, :component
99

10-
def initialize(component: false, component_name: "Component", parent_component: "Phlex::HTML", whitespace: true)
10+
def initialize(component: false, component_name: "Component", parent_component: "Phlex::HTML", whitespace: true, svg_param: "s")
1111
@component = component
1212
@component_name = safe_constant_name(component_name)
1313
@parent_component = safe_constant_name(parent_component)
1414
@whitespace = whitespace
15+
@svg_param = svg_param
1516
end
1617

1718
def safe_constant_name(name)

gem/lib/phlexing/template_generator.rb

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,10 @@ def handle_html_element_node(node, level)
147147
out << tag_name(node)
148148
out << handle_attributes(node)
149149

150+
params = node.name == "svg" ? options.svg_param : nil
151+
150152
if node.children.any?
151-
block { handle_children(node, level) }
153+
block(params) { handle_children(node, level) }
152154
end
153155

154156
out << newline
@@ -189,6 +191,22 @@ def handle_element_node(node, level)
189191
out << newline if level == 1
190192
end
191193

194+
def handle_svg_node(node, level)
195+
node.children.each do |child|
196+
child.traverse do |subchild|
197+
subchild.name = SVG_ELEMENTS[subchild.name] if SVG_ELEMENTS.key?(subchild.name)
198+
subchild.name = subchild.name.prepend("#{options.svg_param}.") # rubocop:disable Style/RedundantSelfAssignment
199+
end
200+
end
201+
202+
whitespace_before = options.whitespace
203+
options.whitespace = false
204+
205+
handle_element_node(node, level)
206+
207+
options.whitespace = whitespace_before
208+
end
209+
192210
def handle_document_node(node, level)
193211
handle_children(node, level)
194212
end
@@ -204,7 +222,11 @@ def handle_node(node, level = 0)
204222
in Nokogiri::XML::Text
205223
handle_text_node(node)
206224
in Nokogiri::XML::Element
207-
handle_element_node(node, level)
225+
if node.name == "svg"
226+
handle_svg_node(node, level)
227+
else
228+
handle_element_node(node, level)
229+
end
208230
in Nokogiri::HTML4::Document | Nokogiri::HTML4::DocumentFragment | Nokogiri::XML::DTD
209231
handle_document_node(node, level)
210232
in Nokogiri::XML::Comment
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# frozen_string_literal: true
2+
3+
require_relative "../../test_helper"
4+
5+
class Phlexing::Converter::SvgTest < Minitest::Spec
6+
it "converts SVG" do
7+
html = %(
8+
<svg>
9+
<path d="123"></path>
10+
</svg>
11+
)
12+
13+
expected = <<~PHLEX.strip
14+
svg { |s| s.path(d: "123") }
15+
PHLEX
16+
17+
assert_phlex_template expected, html
18+
end
19+
20+
it "converts SVG with attributes" do
21+
html = %(
22+
<svg one="attribute" two="attributes">
23+
<path d="123"></path>
24+
</svg>
25+
)
26+
27+
expected = <<~PHLEX.strip
28+
svg(one: "attribute", two: "attributes") { |s| s.path(d: "123") }
29+
PHLEX
30+
31+
assert_phlex_template expected, html
32+
end
33+
34+
it "converts SVG with ERB interpolation" do
35+
html = %(
36+
<svg one="<%= interpolate %>" two="<%= method_call(123) %>">
37+
<path d="123"></path>
38+
</svg>
39+
)
40+
41+
expected = <<~PHLEX.strip
42+
svg(one: interpolate, two: method_call(123)) { |s| s.path(d: "123") }
43+
PHLEX
44+
45+
assert_phlex_template expected, html do
46+
assert_locals "interpolate"
47+
assert_instance_methods "method_call"
48+
end
49+
end
50+
51+
it "converts SVG with case-sensitive" do
52+
html = %(
53+
<svg>
54+
<feSpecularLighting>
55+
<fePointLight/>
56+
</feSpecularLighting>
57+
</svg>
58+
)
59+
60+
expected = <<~PHLEX.strip
61+
svg { |s| s.feSpecularLighting { s.fePointLight } }
62+
PHLEX
63+
64+
assert_phlex_template expected, html
65+
end
66+
67+
it "nested SVG" do
68+
html = %(
69+
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="5cm" height="5cm">
70+
<desc>Two groups, each of two rectangles</desc>
71+
<g id="group1" fill="red">
72+
<rect x="1cm" y="1cm" width="1cm" height="1cm"/>
73+
<rect x="3cm" y="1cm" width="1cm" height="1cm"/>
74+
</g>
75+
76+
<g id="group2" fill="blue">
77+
<rect x="1cm" y="3cm" width="1cm" height="1cm"/>
78+
<rect x="3cm" y="3cm" width="1cm" height="1cm"/>
79+
</g>
80+
81+
<rect x=".01cm" y=".01cm" width="4.98cm" height="4.98cm" fill="none" stroke="blue" stroke-width=".02cm"/>
82+
</svg>
83+
)
84+
85+
expected = <<~PHLEX.strip
86+
svg(
87+
xmlns: "http://www.w3.org/2000/svg",
88+
version: "1.1",
89+
width: "5cm",
90+
height: "5cm"
91+
) do |s|
92+
s.desc { "Two groups, each of two rectangles" }
93+
s.g(id: "group1", fill: "red") do
94+
s.rect(x: "1cm", y: "1cm", width: "1cm", height: "1cm")
95+
s.rect(x: "3cm", y: "1cm", width: "1cm", height: "1cm")
96+
end
97+
s.g(id: "group2", fill: "blue") do
98+
s.rect(x: "1cm", y: "3cm", width: "1cm", height: "1cm")
99+
s.rect(x: "3cm", y: "3cm", width: "1cm", height: "1cm")
100+
end
101+
s.rect(
102+
x: ".01cm",
103+
y: ".01cm",
104+
width: "4.98cm",
105+
height: "4.98cm",
106+
fill: "none",
107+
stroke: "blue",
108+
stroke_width: ".02cm"
109+
)
110+
end
111+
PHLEX
112+
113+
assert_phlex_template expected, html
114+
end
115+
end

0 commit comments

Comments
 (0)