Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
This repository was archived by the owner on Nov 8, 2022. It is now read-only.

feat(editor-export): quote block#292

Merged
mydearxym merged 2 commits intodevfromeditor-quote
Feb 26, 2021
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletionslib/helper/converter/editor_to_html/class.ex
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,15 @@ defmodule Helper.Converter.EditorToHTML.Class do
"eyebrow_title" => "eyebrow-title",
"footer_title" => "footer-title"
},
# quote block
"quote" => %{
"short_wrapper" => "quote-short",
"long_wrapper" => "quote-long",
"text" => "quote__text",
"caption" => "quote-caption",
"caption_line" => "quote-caption__line",
"caption_text" => "quote-caption__text"
},
# list
"list" => %{
"wrapper" => "list-wrapper",
Expand Down
57 changes: 57 additions & 0 deletionslib/helper/converter/editor_to_html/frags/quote.ex
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
defmodule Helper.Converter.EditorToHTML.Frags.Quote do
@moduledoc """
parse editor.js's block fragments, use for test too

see https://editorjs.io/
"""
import Helper.Validator.Guards, only: [g_none_empty_str: 1]

alias Helper.Types, as: T
alias Helper.Converter.EditorToHTML.Class

@class get_in(Class.article(), ["quote"])

@spec get(T.editor_quote()) :: T.html()
def get(%{"mode" => "short", "text" => text}) do
wrapper_class = @class["short_wrapper"]
text_class = @class["text"]

~s(<blockquote class="#{wrapper_class}">
<div class="#{text_class}">#{text}</div>
</blockquote>)
end

def get(%{"mode" => "long", "text" => text, "caption" => caption})
when g_none_empty_str(caption) do
wrapper_class = @class["long_wrapper"]
text_class = @class["text"]

caption = frag(:caption, caption)

~s(<blockquote class="#{wrapper_class}">
<div class="#{text_class}">#{text}</div>
#{caption}
</blockquote>)
end

def get(%{"mode" => "long", "text" => text}) do
wrapper_class = @class["long_wrapper"]
text_class = @class["text"]

~s(<blockquote class="#{wrapper_class}">
<div class="#{text_class}">#{text}</div>
</blockquote>)
end

@spec frag(:caption, String.t()) :: T.html()
def frag(:caption, caption) do
caption_class = @class["caption"]
caption_line_class = @class["caption_line"]
caption_text_class = @class["caption_text"]

~s(<div class="#{caption_class}">
<div class="#{caption_line_class}"/>
<div class="#{caption_text_class}">#{caption}</div>
</div>)
end
end
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

89 changes: 89 additions & 0 deletionslib/helper/converter/editor_to_html/frontend_test/styles.css
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -277,3 +277,92 @@ td {
}

/* table block end */

/* quote block */

.article-viewer-wrapper .quote-short {
margin-left: 15px;
min-height: 0;
border: none;
border-radius: 0;
border-left: 4px solid;
border-left-color: #388ae5;
color: grey;
box-shadow: none;
}

.article-viewer-wrapper .quote__text {
line-height: 1.8em;
min-height: 30px;
border: none;
border-radius: 0;
color: grey;
box-shadow: none;
padding: 10px 12px;
outline: none;
width: 100%;
box-sizing: border-box;
}

.article-viewer-wrapper .quote-long {
position: relative;
border: 2px solid;
border-left: 1px solid;
border-right: 1px solid;
border-bottom: 1px solid;
border-color: lightgrey;
margin: 15px;
margin-left: 22px;
margin-right: 42px;
min-height: 140px;
padding: 6px 30px;
padding-bottom: 60px;
border-top-right-radius: 12px;
border-bottom-left-radius: 12px;
}

.article-viewer-wrapper .quote-long::before {
position: absolute;
content: "\201C";
font-size: 50px;
color: #e8e7e7;
left: -16px;
top: 9px;
height: 35px;
background: white;
}

.article-viewer-wrapper .quote-long::after {
position: absolute;
content: "\201D";
font-size: 42px;
color: #e8e7e7;
right: -13px;
bottom: 9px;
height: 28px;
background: white;
}

.article-viewer-wrapper .quote-caption {
position: absolute;
bottom: 10px;
right: 42px;
display: flex;
align-items: center;
}

.article-viewer-wrapper .quote-caption__line {
width: 30px;
height: 1px;
margin-right: 10px;
background: #bbb;
}

.article-viewer-wrapper .quote-caption__text {
cursor: text;
outline: none;
color: grey;
font-size: 14px;
}

/* quote block end */
2 changes: 2 additions & 0 deletionslib/helper/converter/editor_to_html/index.ex
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -45,6 +45,8 @@ defmodule Helper.Converter.EditorToHTML do

defp parse_block(%{"type" => "header", "data" => data}), do: Frags.Header.get(data)

defp parse_block(%{"type" => "quote", "data" => data}), do: Frags.Quote.get(data)

defp parse_block(%{"type" => "list", "data" => data}) do
%{"items" => items, "mode" => mode} = data

Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,9 @@ defmodule Helper.Converter.EditorToHTML.Validator.EditorSchema do
# header
@valid_header_level [1, 2, 3]

# quote
@valid_quote_mode ["short", "long"]

# list
@valid_list_mode ["checklist", "order_list", "unorder_list"]
@valid_list_label_type ["green", "red", "warn", "default"]
Expand All@@ -12,6 +15,7 @@ defmodule Helper.Converter.EditorToHTML.Validator.EditorSchema do
# table
@valid_table_align ["left", "center", "right"]

@spec get(String.t()) :: map
def get("editor") do
%{
"time" => [:number],
Expand All@@ -31,6 +35,15 @@ defmodule Helper.Converter.EditorToHTML.Validator.EditorSchema do

def get("paragraph"), do: %{"text" => [:string]}

def get("quote") do
%{
"text" => [:string],
"mode" => [enum: @valid_quote_mode],
"caption" => [:string, required: false]
}
end

@spec get(String.t()) :: [parent: map, item: map]
def get("list") do
[
parent: %{"mode" => [enum: @valid_list_mode], "items" => [:list]},
Expand Down
4 changes: 2 additions & 2 deletionslib/helper/converter/editor_to_html/validator/index.ex
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,7 @@ defmodule Helper.Converter.EditorToHTML.Validator do
alias Converter.EditorToHTML.Validator.EditorSchema

# blocks with no children items
@simple_blocks ["header", "paragraph"]
@normal_blocks ["header", "paragraph", "quote"]
# blocks with "items" fields
@complex_blocks ["list", "table"]

Expand DownExpand Up@@ -54,7 +54,7 @@ defmodule Helper.Converter.EditorToHTML.Validator do
end

# validate block which have no nested items
defp validate_block(%{"type" => type, "data" => data}) when type in @simple_blocks do
defp validate_block(%{"type" => type, "data" => data}) when type in @normal_blocks do
validate_with(type, EditorSchema.get(type), data)
end

Expand Down
3 changes: 3 additions & 0 deletionslib/helper/converter/html_sanitizer.ex
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -45,6 +45,9 @@ defmodule Helper.Converter.HtmlSanitizer do
Meta.allow_tag_with_these_attributes("th", ["class"])
Meta.allow_tag_with_these_attributes("td", ["class", "style"])

# blockquote
Meta.allow_tag_with_these_attributes("blockquote", ["class"])

Meta.allow_tag_with_these_attributes("svg", [
"t",
"p-id",
Expand Down
10 changes: 10 additions & 0 deletionslib/helper/types.ex
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,6 +38,16 @@ defmodule Helper.Types do
footerTitle: String.t()
}

@typep editor_quote_mode :: :short | :long
@typedoc """
editor.js's quote tool data format
"""
@type editor_quote :: %{
required(:text) => String.t(),
required(:mode) => editor_quote_mode,
caption: String.t()
}

@typedoc """
valid editor.js's list item indent
"""
Expand Down
2 changes: 2 additions & 0 deletionslib/helper/validator/guards.ex
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,4 +4,6 @@ defmodule Helper.Validator.Guards do
"""
defguard g_pos_int(value) when is_integer(value) and value >= 0
defguard g_not_nil(value) when not is_nil(value)

defguard g_none_empty_str(value) when is_binary(value) and byte_size(value) > 0
end
125 changes: 125 additions & 0 deletionstest/helper/converter/editor_to_html_test/quote_test.exs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
defmodule GroupherServer.Test.Helper.Converter.EditorToHTML.Quote do
@moduledoc false

use GroupherServerWeb.ConnCase, async: true

alias Helper.Converter.EditorToHTML.Class
alias Helper.Converter.EditorToHTML, as: Parser

alias Helper.Utils

@root_class Class.article()
@class get_in(@root_class, ["quote"])

describe "[quote block]" do
defp set_data(mode, text, caption \\ nil) do
data =
case caption do
nil ->
%{
"mode" => mode,
"text" => text
}

_ ->
%{
"mode" => mode,
"text" => text,
"caption" => caption
}
end

%{
"time" => 1_567_250_876_713,
"blocks" => [
%{
"type" => "quote",
"data" => data
}
],
"version" => "2.15.0"
}
end

@tag :wip2
test "short quote parse should work" do
editor_json = set_data("short", "short quote")
{:ok, editor_string} = Jason.encode(editor_json)
{:ok, converted} = Parser.to_html(editor_string)

short_wrapper_class = @class["short_wrapper"]
caption_class = @class["caption"]

assert Utils.str_occurence(converted, short_wrapper_class) == 1
assert Utils.str_occurence(converted, "</blockquote>") == 1
assert Utils.str_occurence(converted, caption_class) == 0
end

@tag :wip2
test "long quote parse should work" do
editor_json = set_data("long", "long quote", "caption")
{:ok, editor_string} = Jason.encode(editor_json)
{:ok, converted} = Parser.to_html(editor_string)

long_wrapper_class = @class["long_wrapper"]
caption_text_class = @class["caption_text"]

assert Utils.str_occurence(converted, long_wrapper_class) == 1
assert Utils.str_occurence(converted, "</blockquote>") == 1
assert Utils.str_occurence(converted, caption_text_class) == 1
end

@tag :wip2
test "long quote without caption parse should work" do
editor_json = set_data("long", "long quote")
{:ok, editor_string} = Jason.encode(editor_json)

{:ok, converted} = Parser.to_html(editor_string)

long_wrapper_class = @class["long_wrapper"]
caption_text_class = @class["caption_text"]

assert Utils.str_occurence(converted, long_wrapper_class) == 1
assert Utils.str_occurence(converted, "</blockquote>") == 1
assert Utils.str_occurence(converted, caption_text_class) == 0
end

@tag :wip2
test "long quote without empty caption parse should work" do
editor_json = set_data("long", "long quote", "")
{:ok, editor_string} = Jason.encode(editor_json)

{:ok, converted} = Parser.to_html(editor_string)

long_wrapper_class = @class["long_wrapper"]
caption_text_class = @class["caption_text"]

assert Utils.str_occurence(converted, long_wrapper_class) == 1
assert Utils.str_occurence(converted, "</blockquote>") == 1
assert Utils.str_occurence(converted, caption_text_class) == 0
end

# @editor_json %{
# "time" => 1_567_250_876_713,
# "blocks" => [
# %{
# "type" => "paragraph",
# "data" => %{
# "text" => []
# }
# }
# ],
# "version" => "2.15.0"
# }
@tag :wip2
test "invalid quote should have invalid hint" do
editor_json = set_data("none_exsit", "long quote", "")
{:ok, editor_string} = Jason.encode(editor_json)
{:error, error} = Parser.to_html(editor_string)

assert error == [
%{block: "quote", field: "mode", message: "should be: short | long"}
]
end
end
end

[8]ページ先頭

©2009-2025 Movatter.jp