Skip to content

Commit

Permalink
Added support for rendering MJML components
Browse files Browse the repository at this point in the history
  • Loading branch information
akoutmos committed Apr 15, 2022
1 parent ed52c16 commit 189272b
Showing 5 changed files with 142 additions and 0 deletions.
24 changes: 24 additions & 0 deletions lib/engines/mjml.ex
Original file line number Diff line number Diff line change
@@ -23,11 +23,35 @@ defmodule MjmlEEx.Engines.Mjml do
defdelegate handle_text(state, meta, text), to: EEx.Engine

@impl true
def handle_expr(state, "=", {:render_component, _, [{:__aliases__, _, module}]}) do
do_render_component(state, module, [])
end

def handle_expr(state, "=", {:render_component, _, [{:__aliases__, _, module}, opts]}) do
do_render_component(state, module, opts)
end

def handle_expr(_state, _marker, {:render_component, _, _}) do
raise "render_component can only be invoked inside of an <%= ... %> expression"
end

def handle_expr(state, marker, expr) do
encoded_code = Utils.encode_expression(marker, expr)
encoded_expression = "__MJML_EEX_START__:#{encoded_code}:__MJML_EEX_END__"

%{binary: binary} = state
%{state | binary: [encoded_expression | binary]}
end

defp do_render_component(state, module_alias_list, opts) do
{mjml_component, _} =
module_alias_list
|> Module.concat()
|> apply(:render, [opts])
|> EEx.compile_string(engine: MjmlEEx.Engines.Mjml, line: 1, trim: true)
|> Code.eval_quoted()

%{binary: binary} = state
%{state | binary: [mjml_component | binary]}
end
end
26 changes: 26 additions & 0 deletions lib/mjml_eex/component.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
defmodule MjmlEEx.Component do
@moduledoc """
This module allows you to define a reusable MJML component that
can be injected into an MJML template prior to it being
rendered into HTML.
"""

@doc """
Returns the MJML markup for a particular component. This callback does
not take in any parameters
"""
@callback render(opts :: keyword()) :: String.t()

defmacro __using__(_opts) do
quote do
@behaviour MjmlEEx.Component

@impl true
def render(_opts) do
raise "Your MjmlEEx component must implement a render/1 callback"
end

defoverridable MjmlEEx.Component
end
end
end
18 changes: 18 additions & 0 deletions test/test_components/attribute_block.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
defmodule MjmlEEx.TestComponents.AttributeBlock do
@moduledoc """
This module defines the MJML component for the shared attibute block.
"""

use MjmlEEx.Component

@impl true
def render(_opts) do
"""
<mj-attributes>
<mj-all font-family="Montserrat, Helvetica, Arial, sans-serif"></mj-all>
<mj-text font-weight="400" font-size="16px" color="#000000" line-height="24px"></mj-text>
<mj-section padding="0px"></mj-section>
</mj-attributes>
"""
end
end
22 changes: 22 additions & 0 deletions test/test_components/head_block.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
defmodule MjmlEEx.TestComponents.HeadBlock do
@moduledoc """
This module defines the MJML component for the shared head block.
"""

use MjmlEEx.Component

@impl true
def render(opts) do
# Merge default options with whatever was passed in
defaults = [title: "Welcome!", font: "Roboto"]
opts = Keyword.merge(defaults, opts)

"""
<mj-head>
<mj-title>#{opts[:title]}</mj-title>
<mj-font name="#{opts[:font]}" href="https://fonts.googleapis.com/css?family=Montserrat:300,400,500"></mj-font>
<%= render_component MjmlEEx.TestComponents.AttributeBlock %>
</mj-head>
"""
end
end
52 changes: 52 additions & 0 deletions test/test_templates/component_template.mjml.eex
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<mjml>
<%= render_component(MjmlEEx.TestComponents.HeadBlock, [title: "Hello!"]) %>
<mj-body background-color="#F2F2F2">
<mj-section padding="10px 0 20px 0">
<mj-column>
<mj-text align="center" color="#9B9B9B" font-size="11px">Writing A Good Headline For Your Advertisement</mj-text>
</mj-column>
</mj-section>
<mj-section padding="20px 20px 0 20px" background-color="#FFFFFF">
<mj-column width="35%">
<mj-text align="left" font-size="20px" font-weight="500">// BR&amp;AND</mj-text>
</mj-column>
<mj-column width="65%">
<mj-text align="right" font-size="11px"><a href="#" style="color: #000000; text-decoration: none;">HOME</a>&nbsp;&nbsp;&nbsp;/&nbsp;&nbsp;&nbsp;<a href="#" style="color: #000000; text-decoration: none;">SERVICE</a>&nbsp;&nbsp;&nbsp;/&nbsp;&nbsp;&nbsp;<a href="#" style="color: #000000; text-decoration: none;">THIRD</a></mj-text>
</mj-column>
</mj-section>
<mj-section padding="20px 20px 0 20px" background-color="#FFFFFF">
<mj-column>
<mj-text align="center" font-weight="300" padding="30px 40px 10px 40px" font-size="32px" line-height="40px" color="#5FA91D">Free Advertising For Your Online Business.</mj-text>
</mj-column>
</mj-section>
<mj-section padding="10px 20px" background-color="#FFFFFF">
<mj-column>
<mj-divider width="30px" border-width="3px" border-color="#9B9B9B"></mj-divider>
</mj-column>
</mj-section>
<mj-section padding="0 20px 20px 20px" background-color="#FFFFFF">
<mj-column width="80%">
<mj-text align="center" padding-top="10px" font-weight="500" padding="0px">A Right Media Mix Can Make The Difference.</mj-text>
</mj-column>
</mj-section>
<mj-section background-url="http://nimus.de/share/tpl-card/bg.jpg" vertical-align="middle" background-size="cover" background-repeat="no-repeat">
<mj-column width="100%">
<mj-image src="http://nimus.de/share/tpl-card/lineshadow.png" alt="" align="center" border="none" padding="0px"></mj-image>
<mj-text align="center" padding="50px 40px 0 40px" font-weight="300">Marketers/advertisers usually focus their efforts on the people responsible for making the purchase. In many cases, this is an effective approach but in other cases it can make for a totally useless marketing campaign.</mj-text>
<mj-button align="center" background-color="#5FA91D" color="#FFFFFF" border-radius="2px" href="#" inner-padding="15px 30px" padding-bottom="100px" padding-top="20px">
<%= @call_to_action_text %>
</mj-button>
</mj-column>
</mj-section>
<mj-section padding="50px 0 0 0" background-color="#FFFFFF">
<mj-column>
<mj-image src="http://nimus.de/share/tpl-card/bottom.png" alt="bottom border" align="center" border="none" padding="0px"></mj-image>
</mj-column>
</mj-section>
<mj-section padding="10px 0 20px 0">
<mj-column>
<mj-text align="center" color="#9B9B9B" font-size="11px"><a href="#" style="color: #9B9B9B;">Unsubscribe</a> from this newsletter<br />52 Edison Court Suite 259 / East Aidabury / Cambodi<br /> <a href="#" style="color: #9B9B9B; text-decoration:none;">Made by svenhaustein.de</a></mj-text>
</mj-column>
</mj-section>
</mj-body>
</mjml>

0 comments on commit 189272b

Please sign in to comment.