html-pipeline-task_list 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "task_list",
3
+ "version": "1.0.2",
4
+ "description": "GitHub-flavored-Markdown TaskList components",
5
+ "homepage": "https://github.com/codetre/html-pipeline-task_list",
6
+ "repository": "https://github.com/codetre/html-pipeline-task_list",
7
+ "license": "MIT",
8
+ "dependencies": {
9
+ "jquery": "^3.4.1",
10
+ "jquery-ujs": "^1.2.2"
11
+ },
12
+ "devDependencies": {
13
+ "node-qunit-puppeteer": "^2.0.1",
14
+ "qunit": "^2.9.3"
15
+ },
16
+ "scripts": {
17
+ "server": "node ./test/server.js .",
18
+ "test": "node-qunit-puppeteer ./test/qunit.html 10000 --no-sandbox"
19
+ },
20
+ "main": [
21
+ "app/assets/javascripts/task_list.js",
22
+ "app/assets/stylesheets/task_list.scss"
23
+ ],
24
+ "ignore": [
25
+ ".gitignore",
26
+ ".travis.yml",
27
+ "*.gemspec",
28
+ "*.md",
29
+ "config.ru",
30
+ "Gemfile",
31
+ "lib/",
32
+ "Rakefile",
33
+ "script/",
34
+ "test/"
35
+ ]
36
+ }
@@ -0,0 +1,109 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <script type="text/javascript" src="dist/jquery.js"></script>
6
+ <script type="text/javascript" src="https://code.jquery.com/jquery-migrate-3.1.0.js"></script>
7
+ <script type="text/javascript" src="dist/jquery-ujs.js"></script>
8
+ <script type="text/javascript" src="dist/task_list.js"></script>
9
+ <script type="text/javascript">
10
+ function logEvent(event) {
11
+ console.log(event)
12
+ $('.log').prepend("<li>" + event.type + "</li>")
13
+ }
14
+
15
+ setInterval(function() {
16
+ if (!$('.log li:first').first().hasClass("timestamp"))
17
+ $('.log').prepend("<li class='timestamp'><time>" + new Date + "</time></li>")
18
+ }, 3000)
19
+
20
+ $(document).on('tasklist:enabled', logEvent)
21
+ $(document).on('tasklist:disabled', logEvent)
22
+ $(document).on('tasklist:change', '.js-task-list-field', function(event){
23
+ logEvent(event)
24
+ $(this).closest('.js-task-list-container').taskList('enable')
25
+ })
26
+ $(document).on('tasklist:changed', '.js-task-list-field', function(event){
27
+ logEvent(event)
28
+ $(this).closest('form').submit()
29
+ })
30
+
31
+ $(document).on('ajaxStart', logEvent)
32
+ $(document).on('ajaxError', logEvent)
33
+ $(document).on('ajaxComplete', logEvent)
34
+ $(document).on('ajaxSuccess', function(event){
35
+ logEvent(event)
36
+ $(this).taskList()
37
+ })
38
+ </script>
39
+
40
+ <style>
41
+ body {
42
+ font: 13px Helvetica;
43
+ padding: 20px;
44
+ }
45
+ input {
46
+ font-size: 20px;
47
+ }
48
+ time {
49
+ font-size: 10px;
50
+ font-style: oblique;
51
+ }
52
+
53
+ .task-list {
54
+ list-style-type: none;
55
+ }
56
+ .task-list .task-list-item {
57
+ opacity: 0.75;
58
+ }
59
+ .task-list .task-list-item.enabled {
60
+ opacity: 1.0;
61
+ }
62
+
63
+ .log {
64
+ position: absolute;
65
+ top: 20px;
66
+ right: 50px;
67
+ color: #222;
68
+ list-style: none;
69
+ margin: 0;
70
+ padding: 0;
71
+ }
72
+ </style>
73
+ </head>
74
+ <body>
75
+ <div class="js-task-list-container is-task-list-enabled">
76
+ <div class="markdown">
77
+ <ul class="task-list">
78
+ <li class="task-list-item enabled">
79
+ <input type="checkbox" class="task-list-item-checkbox" enabled />
80
+ I'm a task list item
81
+ </li>
82
+ <li class="task-list-item enabled">
83
+ <input type="checkbox" class="task-list-item-checkbox" enabled />
84
+ with non-breaking space
85
+ </li>
86
+ <li class="task-list-item enabled">
87
+ <input type="checkbox" class="task-list-item-checkbox" enabled checked />
88
+ completed, lower
89
+ </li>
90
+ <li class="task-list-item enabled">
91
+ <input type="checkbox" class="task-list-item-checkbox" enabled checked />
92
+ completed capitalized
93
+ </li>
94
+ </ul>
95
+ </div>
96
+ <form action="/update" method="POST" data-remote="true" data-type="json">
97
+ <textarea name="comment[body]" class="js-task-list-field" cols="40" rows="10">
98
+ - [ ] I'm a task list item
99
+ - [ ] with non-breaking space
100
+ - [x] completed, lower
101
+ - [X] completed capitalized</textarea>
102
+ </form>
103
+ </div>
104
+
105
+ <ul class="log">
106
+ </ul>
107
+ </body>
108
+ </html>
109
+
@@ -0,0 +1 @@
1
+ ../../node_modules/jquery-ujs/src/rails.js
@@ -0,0 +1 @@
1
+ ../../node_modules/jquery/dist/jquery.js
@@ -0,0 +1 @@
1
+ ../../node_modules/qunit/qunit/qunit.css
@@ -0,0 +1 @@
1
+ ../../node_modules/qunit/qunit/qunit.js
@@ -0,0 +1 @@
1
+ ../../app/assets/javascripts/task_list.js
@@ -0,0 +1,197 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.expand_path('../../../test_helper', __dir__)
4
+ require 'html/pipeline/task_list/filter'
5
+
6
+ class HTML::Pipeline::TaskList::FilterTest < Minitest::Test
7
+ def setup
8
+ @pipeline = HTML::Pipeline.new [
9
+ HTML::Pipeline::MarkdownFilter,
10
+ HTML::Pipeline::TaskList::Filter
11
+ ], {}, {}
12
+
13
+ @context = {}
14
+ @item_selector = 'input.task-list-item-checkbox[type=checkbox]'
15
+ end
16
+
17
+ def test_filters_items_in_a_list
18
+ text = <<~MARKDOWN
19
+ - [ ] incomplete
20
+ - [x] complete
21
+ MARKDOWN
22
+
23
+ assert_equal 2, filter(text)[:output].css(@item_selector).size
24
+ end
25
+
26
+ def test_filters_items_with_html_contents
27
+ text = <<~MARKDOWN
28
+ - [ ] incomplete **with bold** text
29
+ - [x] complete __with italic__ text
30
+ MARKDOWN
31
+
32
+ assert_equal 2, filter(text)[:output].css(@item_selector).size
33
+ end
34
+
35
+ def test_filters_items_in_a_list_wrapped_in_paras
36
+ # See issue #7951 for details.
37
+ text = <<~MARKDOWN
38
+ - [ ] one
39
+ - [ ] this one will be wrapped in a para
40
+
41
+ - [ ] this one too, wtf
42
+ MARKDOWN
43
+
44
+ assert_equal 3, filter(text)[:output].css(@item_selector).size
45
+ end
46
+
47
+ def test_populates_result_with_task_list_items
48
+ text = <<~MARKDOWN
49
+ - [ ] incomplete
50
+ - [x] complete
51
+ MARKDOWN
52
+
53
+ result = filter(text)
54
+ assert !result[:task_list_items].empty?
55
+ incomplete, complete = result[:task_list_items]
56
+
57
+ assert incomplete
58
+ assert !incomplete.complete?
59
+
60
+ assert complete
61
+ assert complete.complete?
62
+ end
63
+
64
+ def test_skips_lists_in_code_blocks
65
+ code = <<~MARKDOWN
66
+ ```
67
+ - [ ] incomplete
68
+ - [x] complete
69
+ ```
70
+ MARKDOWN
71
+
72
+ assert filter(code)[:output].css(@item_selector).empty?,
73
+ 'should not have any task list items'
74
+ end
75
+
76
+ def test_handles_encoding_correctly
77
+ unicode = '中文'
78
+ text = <<~MARKDOWN
79
+ - [ ] #{unicode}
80
+ MARKDOWN
81
+
82
+ assert item = filter(text)[:output].css('.task-list-item').pop
83
+ assert_equal unicode, item.text.strip
84
+ end
85
+
86
+ def test_handles_nested_items
87
+ text = <<~MARKDOWN
88
+ - [ ] one
89
+ - [ ] one.one
90
+ MARKDOWN
91
+
92
+ assert filter(text)[:output].css('.task-list-item .task-list-item').pop
93
+ end
94
+
95
+ def test_handles_complicated_nested_items
96
+ text = <<~MARKDOWN
97
+ - [ ] one
98
+ - [ ] one.one
99
+ - [x] one.two
100
+ - [ ] one.two.one
101
+ - [ ] one.two.two
102
+ - [ ] one.three
103
+ - [ ] one.four
104
+ - [ ] two
105
+ - [x] two.one
106
+ - [ ] two.two
107
+ - [ ] three
108
+ MARKDOWN
109
+
110
+ output = filter(text)[:output]
111
+
112
+ assert_equal 6 + 2, output.css('.task-list-item .task-list-item').size
113
+ assert_equal 2, output.css('.task-list-item .task-list-item .task-list-item').size
114
+ end
115
+
116
+ # NOTE: This is an edge case experienced regularly by users using a Swiss
117
+ # German keyboard.
118
+ # See: https://github.com/github/github/pull/18362
119
+ def test_non_breaking_space_between_brackets
120
+ text = "- [\xC2\xA0] ok"
121
+
122
+ assert item = filter(text)[:output].css('.task-list-item').pop, 'item expected'
123
+ assert_equal 'ok', item.text.strip
124
+ end
125
+
126
+ # See: https://github.com/github/github/pull/18362
127
+ def test_non_breaking_space_between_brackets_in_paras
128
+ text = <<~MARKDOWN
129
+ - [\xC2\xA0] one
130
+ - [\xC2\xA0] this one will be wrapped in a para
131
+
132
+ - [\xC2\xA0] this one too, wtf
133
+ MARKDOWN
134
+
135
+ assert_equal 3, filter(text)[:output].css(@item_selector).size
136
+ end
137
+
138
+ def test_capital_x
139
+ text = <<~MARKDOWN
140
+ - [x] lower case
141
+ - [X] capital
142
+ MARKDOWN
143
+
144
+ assert_equal 2, filter(text)[:output].css('[checked]').size
145
+ end
146
+
147
+ def test_skips_task_list_when_no_content
148
+ text = <<~MARKDOWN
149
+ - [x]
150
+ - [X]
151
+ - [ ]
152
+ MARKDOWN
153
+
154
+ result = filter(text)
155
+ assert_nil result[:task_list_items]
156
+ assert result[:output].css(@item_selector).empty?,
157
+ 'should not have any task list items'
158
+ end
159
+
160
+ def test_handles_input_with_a_mix_of_lists_and_task_lists
161
+ text = <<~MARKDOWN
162
+ - Item 1
163
+ - Item 2
164
+
165
+ - [x] Task 1
166
+ - [ ] Task 2
167
+ MARKDOWN
168
+
169
+ result = filter(text)
170
+
171
+ output = result[:output].to_s
172
+ complete, incomplete = result[:task_list_items]
173
+ assert complete.complete?
174
+ refute incomplete.complete?
175
+
176
+ # regular list items
177
+ assert_match %r{<li>\n<p>Item 1</p>\n</li>}, output
178
+ assert_match %r{<li>\n<p>Item 2</p>\n</li>}, output
179
+
180
+ # task list items
181
+ assert_match(
182
+ %r{<p><input type="checkbox" class="task-list-item-checkbox" checked disabled> Task 1</p>},
183
+ output
184
+ )
185
+ assert_match(
186
+ %r{<p><input type="checkbox" class="task-list-item-checkbox" disabled> Task 2</p>},
187
+ output
188
+ )
189
+ end
190
+
191
+ protected
192
+
193
+ def filter(input, context = @context, result = nil)
194
+ result ||= {}
195
+ @pipeline.call(input, context, result)
196
+ end
197
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.expand_path('../../../test_helper', __dir__)
4
+
5
+ class HTML::Pipeline::TaskList::SummaryTest < Minitest::Test
6
+ def setup
7
+ @complete = make_item '[x]', 'complete'
8
+ @incomplete = make_item '[ ]', 'incomplete'
9
+ @items = [@complete, @incomplete]
10
+ @summary = make_summary @items
11
+ end
12
+
13
+ def test_no_items
14
+ summary = make_summary []
15
+ assert !summary.items?, 'no task list items are expected'
16
+ end
17
+
18
+ def test_items
19
+ assert @summary.items?, 'task list items are expected'
20
+ assert_equal 2, @summary.item_count
21
+ end
22
+
23
+ def test_complete_count
24
+ assert_equal 1, @summary.complete_count
25
+ end
26
+
27
+ def test_incomplete_count
28
+ assert_equal 1, @summary.incomplete_count
29
+ end
30
+
31
+ protected
32
+
33
+ def make_item(checkbox_text = '[ ]', source = 'an item!')
34
+ HTML::Pipeline::TaskList::Item.new(checkbox_text, source)
35
+ end
36
+
37
+ def make_summary(items)
38
+ HTML::Pipeline::TaskList::Summary.new(items)
39
+ end
40
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.expand_path('../../test_helper', __dir__)
4
+
5
+ class TaskListTest < Minitest::Test
6
+ Record = Struct.new(:body) do
7
+ def task_list_items
8
+ []
9
+ end
10
+ end
11
+
12
+ def test_has_summary
13
+ assert summary = task_list('- [ ] one').summary, 'summary expected'
14
+ assert_kind_of HTML::Pipeline::TaskList::Summary, summary
15
+ end
16
+
17
+ def test_complete_item
18
+ item = HTML::Pipeline::TaskList::Item.new('[x]', 'complete')
19
+ assert item.complete?, 'expected to be complete'
20
+ end
21
+
22
+ def test_incomplete_item
23
+ item = HTML::Pipeline::TaskList::Item.new('[ ]', 'incomplete')
24
+ assert !item.complete?, 'expected to be incomplete'
25
+ end
26
+
27
+ protected
28
+
29
+ def task_list(text)
30
+ HTML::Pipeline::TaskList.new(Record.new(text))
31
+ end
32
+ end
@@ -0,0 +1,17 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <link rel="stylesheet" href="dist/qunit.css">
6
+ <script type="text/javascript" src="dist/jquery.js"></script>
7
+ <script src="https://code.jquery.com/jquery-migrate-3.1.0.js"></script>
8
+ <script type="text/javascript" src="dist/qunit.js"></script>
9
+ <script type="text/javascript" src="dist/task_list.js"></script>
10
+ <script type="text/javascript" src="unit/events_test.js"></script>
11
+ <script type="text/javascript" src="unit/updates_test.js"></script>
12
+ </head>
13
+ <body>
14
+ <div id="qunit"></div>
15
+ <div id="qunit-fixture"></div>
16
+ </body>
17
+ </html>