From f9ee722c2242666168ea2472fe2db8042081f950 Mon Sep 17 00:00:00 2001 From: Ethan Reesor Date: Tue, 26 Apr 2022 18:29:56 -0500 Subject: [PATCH 01/16] Initial add of indeterminate checkboxes to GLFM Authored by ethan.reesor Changelog: added --- .../markdown/nodes/task_list_item.js | 34 +++++++++++++--- app/assets/javascripts/task_list.js | 9 +++++ doc/user/img/completed_tasks_v13_3.png | Bin 14835 -> 0 bytes doc/user/img/completed_tasks_v13_5.png | Bin 0 -> 19150 bytes doc/user/markdown.md | 19 ++++++--- .../filter/indeterminate_checkbox_filter.rb | 36 +++++++++++++++++ lib/banzai/pipeline/gfm_pipeline.rb | 3 +- spec/features/markdown/copy_as_gfm_spec.rb | 5 +++ .../indeterminate_checkbox_filter_spec.rb | 38 ++++++++++++++++++ 9 files changed, 133 insertions(+), 11 deletions(-) delete mode 100644 doc/user/img/completed_tasks_v13_3.png create mode 100644 doc/user/img/completed_tasks_v13_5.png create mode 100644 lib/banzai/filter/indeterminate_checkbox_filter.rb create mode 100644 spec/lib/banzai/filter/indeterminate_checkbox_filter_spec.rb diff --git a/app/assets/javascripts/behaviors/markdown/nodes/task_list_item.js b/app/assets/javascripts/behaviors/markdown/nodes/task_list_item.js index 10ffce9b1b8730..9760ae0fdd0db5 100644 --- a/app/assets/javascripts/behaviors/markdown/nodes/task_list_item.js +++ b/app/assets/javascripts/behaviors/markdown/nodes/task_list_item.js @@ -5,8 +5,8 @@ export default () => ({ name: 'task_list_item', schema: { attrs: { - done: { - default: false, + state: { + default: null, }, }, defining: true, @@ -18,7 +18,13 @@ export default () => ({ tag: 'li.task-list-item', getAttrs: (el) => { const checkbox = el.querySelector('input[type=checkbox].task-list-item-checkbox'); - return { done: checkbox && checkbox.checked }; + if (checkbox?.matches('.indeterminate')) { + return { state: 'indeterminate' }; + } else if (checkbox?.checked) { + return { state: 'done' }; + } + + return {}; }, }, ], @@ -26,13 +32,31 @@ export default () => ({ return [ 'li', { class: 'task-list-item' }, - ['input', { type: 'checkbox', class: 'task-list-item-checkbox', checked: node.attrs.done }], + [ + 'input', + { + type: 'checkbox', + class: 'task-list-item-checkbox', + checked: node.attrs.state === 'done', + indeterminate: node.attrs.state === 'indeterminate', + }, + ], ['div', { class: 'todo-content' }, 0], ]; }, }, toMarkdown(state, node) { - state.write(`[${node.attrs.done ? 'x' : ' '}] `); + switch (node.attrs.state) { + case 'done': + state.write('[x] '); + break; + case 'indeterminate': + state.write('[-] '); + break; + default: + state.write('[ ] '); + break; + } state.renderContent(node); }, }); diff --git a/app/assets/javascripts/task_list.js b/app/assets/javascripts/task_list.js index 79a30340856b8b..b88c673fc5f05e 100644 --- a/app/assets/javascripts/task_list.js +++ b/app/assets/javascripts/task_list.js @@ -62,13 +62,22 @@ export default class TaskList { .prop('disabled', true); } + updateIndeterminateTaskListItems(e) { + this.getTaskListTarget(e) + .find('.task-list-item-checkbox.indeterminate') + .prop('indeterminate', true) + .prop('disabled', true); + } + disableTaskListItems(e) { this.getTaskListTarget(e).taskList('disable'); + this.updateIndeterminateTaskListItems(); } enableTaskListItems(e) { this.getTaskListTarget(e).taskList('enable'); this.disableNonMarkdownTaskListItems(e); + this.updateIndeterminateTaskListItems(); } enable() { diff --git a/doc/user/img/completed_tasks_v13_3.png b/doc/user/img/completed_tasks_v13_3.png deleted file mode 100644 index b12d95f0a236da771b751594fd094ec598bbb62c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14835 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 diff --git a/doc/user/img/completed_tasks_v13_5.png b/doc/user/img/completed_tasks_v13_5.png new file mode 100644 index 0000000000000000000000000000000000000000..7316069177efcd24ccf2107d7f9e2f23176e7b5b GIT binary patch literal 19150 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 qwwkfBa~ZG9|XxORs}A&wIN1xvX Indeterminate checkboxes [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43208) in GitLab 13.5. + [View this topic in GitLab](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/user/markdown.md#task-lists). You can add task lists anywhere Markdown is supported. @@ -384,22 +386,29 @@ You can add task lists anywhere Markdown is supported. - In all other places, you cannot select the boxes. You must edit the Markdown manually by adding or removing an `x` in the brackets. +Besides complete and incomplete, tasks can also be indeterminate. An +indeterminate checkbox cannot be modified; clicking on an indeterminate checkbox +in an issue, merge request, or comment will have no effect. + To create a task list, follow the format of an ordered or unordered list: ```markdown - [x] Completed task +- [-] Indeterminate task - [ ] Incomplete task - - [ ] Sub-task 1 - - [x] Sub-task 2 + - [x] Sub-task 1 + - [-] Sub-task 2 - [ ] Sub-task 3 1. [x] Completed task +1. [-] Indeterminate task 1. [ ] Incomplete task - 1. [ ] Sub-task 1 - 1. [x] Sub-task 2 + 1. [x] Sub-task 1 + 1. [-] Sub-task 2 + 1. [ ] Sub-task 3 ``` -![Task list as rendered by GitLab](img/completed_tasks_v13_3.png) +![Task list as rendered by GitLab](img/completed_tasks_v13_5.png) ### Table of contents diff --git a/lib/banzai/filter/indeterminate_checkbox_filter.rb b/lib/banzai/filter/indeterminate_checkbox_filter.rb new file mode 100644 index 00000000000000..81a38a2ca364a9 --- /dev/null +++ b/lib/banzai/filter/indeterminate_checkbox_filter.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require "nokogiri" + +module Banzai + module Filter + class IndeterminateCheckboxFilter < HTML::Pipeline::Filter + SELECTOR = "li:contains('[-]')" + REGEX = /^(\s*)\[-\]/.freeze + + BOX = Nokogiri::HTML::DocumentFragment.parse( + '' + ).children.first.freeze + + def call + return doc unless doc.at(SELECTOR) + + doc.css(SELECTOR).each do |li| + next unless li.content.match(REGEX) + + text = li.children.first + next unless text.is_a?(Nokogiri::XML::Text) + next unless text.content.match(REGEX) + + text.add_previous_sibling(BOX.dup) + text.content = text.content.sub(REGEX, '\1') + + li.add_class('task-list-item') + li.parent.add_class('task-list') + end + + doc + end + end + end +end diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb index 5e7c2f64c92fb6..12365d83151900 100644 --- a/lib/banzai/pipeline/gfm_pipeline.rb +++ b/lib/banzai/pipeline/gfm_pipeline.rb @@ -36,8 +36,9 @@ def self.filters Filter::EmojiFilter, Filter::CustomEmojiFilter, Filter::TaskListFilter, + Filter::IndeterminateCheckboxFilter, Filter::InlineDiffFilter, - Filter::SetDirectionFilter + Filter::SetDirectionFilter, ] end diff --git a/spec/features/markdown/copy_as_gfm_spec.rb b/spec/features/markdown/copy_as_gfm_spec.rb index d472134a2c780f..1fcc683dd7eca5 100644 --- a/spec/features/markdown/copy_as_gfm_spec.rb +++ b/spec/features/markdown/copy_as_gfm_spec.rb @@ -623,6 +623,11 @@ def foo | b | 0 | 1 | GFM ) + + verify( + 'IndeterminateCheckboxFilter', + '* [-] foo' + ) end alias_method :gfm_to_html, :markdown diff --git a/spec/lib/banzai/filter/indeterminate_checkbox_filter_spec.rb b/spec/lib/banzai/filter/indeterminate_checkbox_filter_spec.rb new file mode 100644 index 00000000000000..aa486191db4348 --- /dev/null +++ b/spec/lib/banzai/filter/indeterminate_checkbox_filter_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' +require 'html/pipeline' +require 'support/helpers/filter_spec_helper' + +RSpec.describe Banzai::Filter::IndeterminateCheckboxFilter, lib: true do + include FilterSpecHelper + shared_examples 'a valid indeterminate task list item' do |html| + it "behaves correctly for `#{html}`" do + filtered = filter("
  • #{html}
") + + expected = <<~HTML.strip_heredoc +
  • + #{described_class::BOX}#{html.sub('[-]', '')} +
+ HTML + + expect(filtered.to_html.delete("\n")).to eq(expected.delete("\n")) + end + end + + shared_examples 'an invalid indeterminate task list item' do |html| + it "does nothing for `#{html}`" do + original = "
  • #{html}
" + filtered = filter(original.dup) + + expect(filtered.to_html.delete("\n")).to eq(original.delete("\n")) + end + end + + it_behaves_like 'a valid indeterminate task list item', '[-] foobar' + it_behaves_like 'a valid indeterminate task list item', '[-] foobar' + + it_behaves_like 'an invalid indeterminate task list item', '[-] foobar' + it_behaves_like 'an invalid indeterminate task list item', 'foo [-] bar' + it_behaves_like 'an invalid indeterminate task list item', 'foo [-] bar' +end -- GitLab From c83a59555ba256628ebb768607691aaa18b7bea5 Mon Sep 17 00:00:00 2001 From: Brett Walker Date: Wed, 4 May 2022 16:29:01 -0500 Subject: [PATCH 02/16] =?UTF-8?q?Switch=20from=20=E2=80=9Cindeterminate?= =?UTF-8?q?=E2=80=9D=20to=20=E2=80=9Cinapplicable=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit and from `[-]` to `[~]` --- .../markdown/nodes/task_list_item.js | 10 +++++----- app/assets/javascripts/task_list.js | 10 +++++----- ...ks_v13_5.png => completed_tasks_v15_0.png} | Bin doc/user/markdown.md | 16 ++++++++-------- ...ter.rb => inapplicable_checkbox_filter.rb} | 8 ++++---- lib/banzai/pipeline/gfm_pipeline.rb | 4 ++-- spec/features/markdown/copy_as_gfm_spec.rb | 4 ++-- ...b => inapplicable_checkbox_filter_spec.rb} | 18 +++++++++--------- 8 files changed, 35 insertions(+), 35 deletions(-) rename doc/user/img/{completed_tasks_v13_5.png => completed_tasks_v15_0.png} (100%) rename lib/banzai/filter/{indeterminate_checkbox_filter.rb => inapplicable_checkbox_filter.rb} (75%) rename spec/lib/banzai/filter/{indeterminate_checkbox_filter_spec.rb => inapplicable_checkbox_filter_spec.rb} (51%) diff --git a/app/assets/javascripts/behaviors/markdown/nodes/task_list_item.js b/app/assets/javascripts/behaviors/markdown/nodes/task_list_item.js index 9760ae0fdd0db5..44f3dd3542ae34 100644 --- a/app/assets/javascripts/behaviors/markdown/nodes/task_list_item.js +++ b/app/assets/javascripts/behaviors/markdown/nodes/task_list_item.js @@ -18,8 +18,8 @@ export default () => ({ tag: 'li.task-list-item', getAttrs: (el) => { const checkbox = el.querySelector('input[type=checkbox].task-list-item-checkbox'); - if (checkbox?.matches('.indeterminate')) { - return { state: 'indeterminate' }; + if (checkbox?.matches('.inapplicable')) { + return { state: 'inapplicable' }; } else if (checkbox?.checked) { return { state: 'done' }; } @@ -38,7 +38,7 @@ export default () => ({ type: 'checkbox', class: 'task-list-item-checkbox', checked: node.attrs.state === 'done', - indeterminate: node.attrs.state === 'indeterminate', + indeterminate: node.attrs.state === 'inapplicable', }, ], ['div', { class: 'todo-content' }, 0], @@ -50,8 +50,8 @@ export default () => ({ case 'done': state.write('[x] '); break; - case 'indeterminate': - state.write('[-] '); + case 'inapplicable': + state.write('[~] '); break; default: state.write('[ ] '); diff --git a/app/assets/javascripts/task_list.js b/app/assets/javascripts/task_list.js index b88c673fc5f05e..3d7db98352ca6d 100644 --- a/app/assets/javascripts/task_list.js +++ b/app/assets/javascripts/task_list.js @@ -62,22 +62,22 @@ export default class TaskList { .prop('disabled', true); } - updateIndeterminateTaskListItems(e) { + updateInapplicableTaskListItems(e) { this.getTaskListTarget(e) - .find('.task-list-item-checkbox.indeterminate') - .prop('indeterminate', true) + .find('.task-list-item-checkbox.inapplicable') + .prop('inapplicable', true) .prop('disabled', true); } disableTaskListItems(e) { this.getTaskListTarget(e).taskList('disable'); - this.updateIndeterminateTaskListItems(); + this.updateInapplicableTaskListItems(); } enableTaskListItems(e) { this.getTaskListTarget(e).taskList('enable'); this.disableNonMarkdownTaskListItems(e); - this.updateIndeterminateTaskListItems(); + this.updateInapplicableTaskListItems(); } enable() { diff --git a/doc/user/img/completed_tasks_v13_5.png b/doc/user/img/completed_tasks_v15_0.png similarity index 100% rename from doc/user/img/completed_tasks_v13_5.png rename to doc/user/img/completed_tasks_v15_0.png diff --git a/doc/user/markdown.md b/doc/user/markdown.md index c2ca11fb213e3f..97bd8512391695 100644 --- a/doc/user/markdown.md +++ b/doc/user/markdown.md @@ -376,7 +376,7 @@ the [Asciidoctor user manual](https://asciidoctor.org/docs/user-manual/#activati ### Task lists -> Indeterminate checkboxes [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43208) in GitLab 13.5. +> Inapplicable checkboxes [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43208) in GitLab 15.0. [View this topic in GitLab](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/user/markdown.md#task-lists). @@ -386,29 +386,29 @@ You can add task lists anywhere Markdown is supported. - In all other places, you cannot select the boxes. You must edit the Markdown manually by adding or removing an `x` in the brackets. -Besides complete and incomplete, tasks can also be indeterminate. An -indeterminate checkbox cannot be modified; clicking on an indeterminate checkbox +Besides complete and incomplete, tasks can also be inapplicable. An +inapplicable checkbox cannot be modified; clicking on an inapplicable checkbox in an issue, merge request, or comment will have no effect. To create a task list, follow the format of an ordered or unordered list: ```markdown - [x] Completed task -- [-] Indeterminate task +- [~] Inapplicable task - [ ] Incomplete task - [x] Sub-task 1 - - [-] Sub-task 2 + - [~] Sub-task 2 - [ ] Sub-task 3 1. [x] Completed task -1. [-] Indeterminate task +1. [~] Inapplicable task 1. [ ] Incomplete task 1. [x] Sub-task 1 - 1. [-] Sub-task 2 + 1. [~] Sub-task 2 1. [ ] Sub-task 3 ``` -![Task list as rendered by GitLab](img/completed_tasks_v13_5.png) +![Task list as rendered by GitLab](img/completed_tasks_v15_0.png) ### Table of contents diff --git a/lib/banzai/filter/indeterminate_checkbox_filter.rb b/lib/banzai/filter/inapplicable_checkbox_filter.rb similarity index 75% rename from lib/banzai/filter/indeterminate_checkbox_filter.rb rename to lib/banzai/filter/inapplicable_checkbox_filter.rb index 81a38a2ca364a9..d526e39e967418 100644 --- a/lib/banzai/filter/indeterminate_checkbox_filter.rb +++ b/lib/banzai/filter/inapplicable_checkbox_filter.rb @@ -4,12 +4,12 @@ module Banzai module Filter - class IndeterminateCheckboxFilter < HTML::Pipeline::Filter - SELECTOR = "li:contains('[-]')" - REGEX = /^(\s*)\[-\]/.freeze + class InapplicableCheckboxFilter < HTML::Pipeline::Filter + SELECTOR = "li:contains('[~]')" + REGEX = /^(\s*)\[~\]/.freeze BOX = Nokogiri::HTML::DocumentFragment.parse( - '' + '' ).children.first.freeze def call diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb index 12365d83151900..31631bd86c8269 100644 --- a/lib/banzai/pipeline/gfm_pipeline.rb +++ b/lib/banzai/pipeline/gfm_pipeline.rb @@ -36,9 +36,9 @@ def self.filters Filter::EmojiFilter, Filter::CustomEmojiFilter, Filter::TaskListFilter, - Filter::IndeterminateCheckboxFilter, + Filter::InapplicableCheckboxFilter, Filter::InlineDiffFilter, - Filter::SetDirectionFilter, + Filter::SetDirectionFilter ] end diff --git a/spec/features/markdown/copy_as_gfm_spec.rb b/spec/features/markdown/copy_as_gfm_spec.rb index 1fcc683dd7eca5..de1fe27b527056 100644 --- a/spec/features/markdown/copy_as_gfm_spec.rb +++ b/spec/features/markdown/copy_as_gfm_spec.rb @@ -625,8 +625,8 @@ def foo ) verify( - 'IndeterminateCheckboxFilter', - '* [-] foo' + 'InapplicableCheckboxFilter', + '* [~] foo' ) end diff --git a/spec/lib/banzai/filter/indeterminate_checkbox_filter_spec.rb b/spec/lib/banzai/filter/inapplicable_checkbox_filter_spec.rb similarity index 51% rename from spec/lib/banzai/filter/indeterminate_checkbox_filter_spec.rb rename to spec/lib/banzai/filter/inapplicable_checkbox_filter_spec.rb index aa486191db4348..c09dff5f55e77d 100644 --- a/spec/lib/banzai/filter/indeterminate_checkbox_filter_spec.rb +++ b/spec/lib/banzai/filter/inapplicable_checkbox_filter_spec.rb @@ -4,15 +4,15 @@ require 'html/pipeline' require 'support/helpers/filter_spec_helper' -RSpec.describe Banzai::Filter::IndeterminateCheckboxFilter, lib: true do +RSpec.describe Banzai::Filter::InapplicableCheckboxFilter, lib: true do include FilterSpecHelper - shared_examples 'a valid indeterminate task list item' do |html| + shared_examples 'a valid inapplicable task list item' do |html| it "behaves correctly for `#{html}`" do filtered = filter("
  • #{html}
") expected = <<~HTML.strip_heredoc
  • - #{described_class::BOX}#{html.sub('[-]', '')} + #{described_class::BOX}#{html.sub('[~]', '')}
HTML @@ -20,7 +20,7 @@ end end - shared_examples 'an invalid indeterminate task list item' do |html| + shared_examples 'an invalid inapplicable task list item' do |html| it "does nothing for `#{html}`" do original = "
  • #{html}
" filtered = filter(original.dup) @@ -29,10 +29,10 @@ end end - it_behaves_like 'a valid indeterminate task list item', '[-] foobar' - it_behaves_like 'a valid indeterminate task list item', '[-] foobar' + it_behaves_like 'a valid inapplicable task list item', '[~] foobar' + it_behaves_like 'a valid inapplicable task list item', '[~] foobar' - it_behaves_like 'an invalid indeterminate task list item', '[-] foobar' - it_behaves_like 'an invalid indeterminate task list item', 'foo [-] bar' - it_behaves_like 'an invalid indeterminate task list item', 'foo [-] bar' + it_behaves_like 'an invalid inapplicable task list item', '[~] foobar' + it_behaves_like 'an invalid inapplicable task list item', 'foo [~] bar' + it_behaves_like 'an invalid inapplicable task list item', 'foo [~] bar' end -- GitLab From 1108b165dea1a269b441e0cbf84fa0279778f40d Mon Sep 17 00:00:00 2001 From: Brett Walker Date: Tue, 28 Jun 2022 11:06:24 -0500 Subject: [PATCH 03/16] Style an inapplicable list item with strikethough and disabled text coloring --- app/assets/stylesheets/framework/typography.scss | 11 +++++++++++ lib/banzai/filter/inapplicable_checkbox_filter.rb | 1 + 2 files changed, 12 insertions(+) diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index b5e0dcd875acce..132974fdad92d1 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -433,6 +433,17 @@ top: 5px; } } + > li.task-list-item.inapplicable { + text-decoration: line-through; + color: $gl-text-color-disabled; + } + } + + ol.task-list { + > li.task-list-item.inapplicable { + text-decoration: line-through; + color: $gl-text-color-disabled; + } } a.with-attachment-icon, diff --git a/lib/banzai/filter/inapplicable_checkbox_filter.rb b/lib/banzai/filter/inapplicable_checkbox_filter.rb index d526e39e967418..13140f24a3bcd9 100644 --- a/lib/banzai/filter/inapplicable_checkbox_filter.rb +++ b/lib/banzai/filter/inapplicable_checkbox_filter.rb @@ -26,6 +26,7 @@ def call text.content = text.content.sub(REGEX, '\1') li.add_class('task-list-item') + li.add_class('inapplicable') li.parent.add_class('task-list') end -- GitLab From b5dc2c92b1936d8a69e3b8eb8772bca267d09f9d Mon Sep 17 00:00:00 2001 From: Brett Walker Date: Tue, 28 Jun 2022 11:32:22 -0500 Subject: [PATCH 04/16] Ensure that list drag icon is always enabled and the add task icon is not, when a list item is inapplicable --- .../stylesheets/framework/typography.scss | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index 132974fdad92d1..0c287ad742ab74 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -433,16 +433,18 @@ top: 5px; } } - > li.task-list-item.inapplicable { - text-decoration: line-through; - color: $gl-text-color-disabled; - } } - ol.task-list { - > li.task-list-item.inapplicable { - text-decoration: line-through; - color: $gl-text-color-disabled; + li.task-list-item.inapplicable { + text-decoration: line-through; + color: $gl-text-color-disabled; + + .drag-icon { + color: $gl-text-color; + } + + .js-add-task { + display: none; } } -- GitLab From c739709ec13f8ecbb318b5f957832565f6cda909 Mon Sep 17 00:00:00 2001 From: Brett Walker Date: Tue, 28 Jun 2022 18:44:25 -0500 Subject: [PATCH 05/16] Refactored the handling of inapplicable items Override the `task_list` gem to handle inapplicable items directly. --- .../markdown/nodes/task_list_item.js | 14 +++- app/assets/javascripts/task_list.js | 3 +- .../stylesheets/framework/typography.scss | 15 ++++- doc/user/img/completed_tasks_v15_0.png | Bin 19150 -> 0 bytes doc/user/img/completed_tasks_v15_2.png | Bin 0 -> 57454 bytes doc/user/markdown.md | 4 +- .../filter/inapplicable_checkbox_filter.rb | 37 ---------- lib/banzai/filter/task_list_filter.rb | 63 +++++++++++++++++- lib/banzai/pipeline/gfm_pipeline.rb | 1 - spec/features/markdown/copy_as_gfm_spec.rb | 7 +- spec/fixtures/markdown.md.erb | 2 + .../inapplicable_checkbox_filter_spec.rb | 38 ----------- .../banzai/filter/task_list_filter_spec.rb | 26 ++++++++ spec/support/matchers/markdown_matchers.rb | 4 +- 14 files changed, 122 insertions(+), 92 deletions(-) delete mode 100644 doc/user/img/completed_tasks_v15_0.png create mode 100644 doc/user/img/completed_tasks_v15_2.png delete mode 100644 lib/banzai/filter/inapplicable_checkbox_filter.rb delete mode 100644 spec/lib/banzai/filter/inapplicable_checkbox_filter_spec.rb diff --git a/app/assets/javascripts/behaviors/markdown/nodes/task_list_item.js b/app/assets/javascripts/behaviors/markdown/nodes/task_list_item.js index 44f3dd3542ae34..095634340c110f 100644 --- a/app/assets/javascripts/behaviors/markdown/nodes/task_list_item.js +++ b/app/assets/javascripts/behaviors/markdown/nodes/task_list_item.js @@ -18,7 +18,7 @@ export default () => ({ tag: 'li.task-list-item', getAttrs: (el) => { const checkbox = el.querySelector('input[type=checkbox].task-list-item-checkbox'); - if (checkbox?.matches('.inapplicable')) { + if (checkbox?.matches('[data-inapplicable]')) { return { state: 'inapplicable' }; } else if (checkbox?.checked) { return { state: 'done' }; @@ -31,14 +31,22 @@ export default () => ({ toDOM(node) { return [ 'li', - { class: 'task-list-item' }, + { + class: () => { + if (node.attrs.state === 'inapplicable') { + return 'task-list-item inapplicable'; + } + + return 'task-list-item'; + }, + }, [ 'input', { type: 'checkbox', class: 'task-list-item-checkbox', checked: node.attrs.state === 'done', - indeterminate: node.attrs.state === 'inapplicable', + 'data-inapplicable': node.attrs.state === 'inapplicable', }, ], ['div', { class: 'todo-content' }, 0], diff --git a/app/assets/javascripts/task_list.js b/app/assets/javascripts/task_list.js index 3d7db98352ca6d..6b6c16284531da 100644 --- a/app/assets/javascripts/task_list.js +++ b/app/assets/javascripts/task_list.js @@ -64,8 +64,7 @@ export default class TaskList { updateInapplicableTaskListItems(e) { this.getTaskListTarget(e) - .find('.task-list-item-checkbox.inapplicable') - .prop('inapplicable', true) + .find('.task-list-item-checkbox[data-inapplicable]') .prop('disabled', true); } diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index 0c287ad742ab74..a1b625f1bd2429 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -435,15 +435,26 @@ } } - li.task-list-item.inapplicable { + li.inapplicable { text-decoration: line-through; color: $gl-text-color-disabled; + ~ li:not(.inapplicable) { + text-decoration: none !important; + color: $gl-text-color; + } + + > p { + text-decoration: line-through; + color: $gl-text-color-disabled; + } + .drag-icon { color: $gl-text-color; } - .js-add-task { + > .js-add-task, + > p > .js-add-task { display: none; } } diff --git a/doc/user/img/completed_tasks_v15_0.png b/doc/user/img/completed_tasks_v15_0.png deleted file mode 100644 index 7316069177efcd24ccf2107d7f9e2f23176e7b5b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19150 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%0DIeEoa6}C!X6;^r0Rv=-0 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
    cug@5t?^zqJ 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 aukOnL literal 0 HcmV?d00001 diff --git a/doc/user/markdown.md b/doc/user/markdown.md index 97bd8512391695..1a7afbfc72a679 100644 --- a/doc/user/markdown.md +++ b/doc/user/markdown.md @@ -376,7 +376,7 @@ the [Asciidoctor user manual](https://asciidoctor.org/docs/user-manual/#activati ### Task lists -> Inapplicable checkboxes [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43208) in GitLab 15.0. +> Inapplicable checkboxes [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85982) in GitLab 15.2. [View this topic in GitLab](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/user/markdown.md#task-lists). @@ -408,7 +408,7 @@ To create a task list, follow the format of an ordered or unordered list: 1. [ ] Sub-task 3 ``` -![Task list as rendered by GitLab](img/completed_tasks_v15_0.png) +![Task list as rendered by GitLab](img/completed_tasks_v15_2.png) ### Table of contents diff --git a/lib/banzai/filter/inapplicable_checkbox_filter.rb b/lib/banzai/filter/inapplicable_checkbox_filter.rb deleted file mode 100644 index 13140f24a3bcd9..00000000000000 --- a/lib/banzai/filter/inapplicable_checkbox_filter.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true - -require "nokogiri" - -module Banzai - module Filter - class InapplicableCheckboxFilter < HTML::Pipeline::Filter - SELECTOR = "li:contains('[~]')" - REGEX = /^(\s*)\[~\]/.freeze - - BOX = Nokogiri::HTML::DocumentFragment.parse( - '' - ).children.first.freeze - - def call - return doc unless doc.at(SELECTOR) - - doc.css(SELECTOR).each do |li| - next unless li.content.match(REGEX) - - text = li.children.first - next unless text.is_a?(Nokogiri::XML::Text) - next unless text.content.match(REGEX) - - text.add_previous_sibling(BOX.dup) - text.content = text.content.sub(REGEX, '\1') - - li.add_class('task-list-item') - li.add_class('inapplicable') - li.parent.add_class('task-list') - end - - doc - end - end - end -end diff --git a/lib/banzai/filter/task_list_filter.rb b/lib/banzai/filter/task_list_filter.rb index 896f67cb8759c4..0991f03630b25a 100644 --- a/lib/banzai/filter/task_list_filter.rb +++ b/lib/banzai/filter/task_list_filter.rb @@ -8,9 +8,70 @@ # - app/assets/javascripts/behaviors/markdown/nodes/task_list_item.js module Banzai module Filter + # TaskList filter replaces task list item markers (`[ ]` and `[x]`) with + # checkboxes, marked up with metadata and behavior. + # + # This should be run on the HTML generated by the Markdown filter, after the + # SanitizationFilter. + # + # Syntax + # ------ + # + # Task list items must be in a list format: + # + # ``` + # - [ ] incomplete + # - [x] complete + # - [~] inapplicable + # ``` + # + # This class overrides TaskList::Filter in the `deckar01-task_list` gem + # to add support for inapplicable task items class TaskListFilter < TaskList::Filter + XPATH = 'descendant-or-self::li[input[@data-inapplicable]] | descendant-or-self::li[p[input[@data-inapplicable]]]' + INAPPLICABLE = '[~]' + INAPPLICABLEPATTERN = /\[~\]/.freeze + + # Pattern used to identify all task list items. + # Useful when you need iterate over all items. + NEWITEMPATTERN = / + ^ + (?:\s*[-+*]|(?:\d+\.))? # optional list prefix + \s* # optional whitespace prefix + ( # checkbox + #{CompletePattern}| + #{IncompletePattern}| + #{INAPPLICABLEPATTERN} + ) + (?=\s) # followed by whitespace + /x.freeze + + # Force the gem's constant to use our new one + superclass.send(:remove_const, :ItemPattern) # rubocop: disable GitlabSecurity/PublicSend + superclass.const_set(:ItemPattern, NEWITEMPATTERN) + + def inapplicable?(item) + !!(item.checkbox_text =~ INAPPLICABLEPATTERN) + end + def render_item_checkbox(item) - "#{super}" + %() + end + + def call + super + + # add class to li for any inapplicable checkboxes + doc.xpath(XPATH) + .each do |li| + li.add_class('inapplicable') + end + + doc end end end diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb index 31631bd86c8269..5e7c2f64c92fb6 100644 --- a/lib/banzai/pipeline/gfm_pipeline.rb +++ b/lib/banzai/pipeline/gfm_pipeline.rb @@ -36,7 +36,6 @@ def self.filters Filter::EmojiFilter, Filter::CustomEmojiFilter, Filter::TaskListFilter, - Filter::InapplicableCheckboxFilter, Filter::InlineDiffFilter, Filter::SetDirectionFilter ] diff --git a/spec/features/markdown/copy_as_gfm_spec.rb b/spec/features/markdown/copy_as_gfm_spec.rb index de1fe27b527056..71f488a43e3d1e 100644 --- a/spec/features/markdown/copy_as_gfm_spec.rb +++ b/spec/features/markdown/copy_as_gfm_spec.rb @@ -109,10 +109,12 @@ <<~GFM, * [ ] Unchecked task * [x] Checked task + * [~] Inapplicable task GFM <<~GFM 1. [ ] Unchecked ordered task 1. [x] Checked ordered task + 1. [~] Inapplicable ordered task GFM ) @@ -623,11 +625,6 @@ def foo | b | 0 | 1 | GFM ) - - verify( - 'InapplicableCheckboxFilter', - '* [~] foo' - ) end alias_method :gfm_to_html, :markdown diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb index 2da16408fbc822..18cd63b7bcb3fd 100644 --- a/spec/fixtures/markdown.md.erb +++ b/spec/fixtures/markdown.md.erb @@ -275,9 +275,11 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - [ ] Incomplete task 1 - [x] Complete task 1 +- [~] Inapplicable task 1 - [ ] Incomplete task 2 - [ ] Incomplete sub-task 1 - [ ] Incomplete sub-task 2 + - [~] Inapplicable sub-task 1 - [x] Complete sub-task 1 - [X] Complete task 2 diff --git a/spec/lib/banzai/filter/inapplicable_checkbox_filter_spec.rb b/spec/lib/banzai/filter/inapplicable_checkbox_filter_spec.rb deleted file mode 100644 index c09dff5f55e77d..00000000000000 --- a/spec/lib/banzai/filter/inapplicable_checkbox_filter_spec.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true - -require 'fast_spec_helper' -require 'html/pipeline' -require 'support/helpers/filter_spec_helper' - -RSpec.describe Banzai::Filter::InapplicableCheckboxFilter, lib: true do - include FilterSpecHelper - shared_examples 'a valid inapplicable task list item' do |html| - it "behaves correctly for `#{html}`" do - filtered = filter("
    • #{html}
    ") - - expected = <<~HTML.strip_heredoc -
    • - #{described_class::BOX}#{html.sub('[~]', '')} -
    - HTML - - expect(filtered.to_html.delete("\n")).to eq(expected.delete("\n")) - end - end - - shared_examples 'an invalid inapplicable task list item' do |html| - it "does nothing for `#{html}`" do - original = "
    • #{html}
    " - filtered = filter(original.dup) - - expect(filtered.to_html.delete("\n")).to eq(original.delete("\n")) - end - end - - it_behaves_like 'a valid inapplicable task list item', '[~] foobar' - it_behaves_like 'a valid inapplicable task list item', '[~] foobar' - - it_behaves_like 'an invalid inapplicable task list item', '[~] foobar' - it_behaves_like 'an invalid inapplicable task list item', 'foo [~] bar' - it_behaves_like 'an invalid inapplicable task list item', 'foo [~] bar' -end diff --git a/spec/lib/banzai/filter/task_list_filter_spec.rb b/spec/lib/banzai/filter/task_list_filter_spec.rb index c89acd1a643d8e..74c77b6aa859e6 100644 --- a/spec/lib/banzai/filter/task_list_filter_spec.rb +++ b/spec/lib/banzai/filter/task_list_filter_spec.rb @@ -10,4 +10,30 @@ expect(doc.xpath('.//li//task-button').count).to eq(2) end + + describe 'inapplicable list items' do + shared_examples 'a valid inapplicable task list item' do |html| + it "behaves correctly for `#{html}`" do + doc = filter("
    • #{html}
    ") + + expect(doc.css('li.inapplicable input[data-inapplicable]').count).to eq(1) + end + end + + shared_examples 'an invalid inapplicable task list item' do |html| + it "does nothing for `#{html}`" do + doc = filter("
    • #{html}
    ") + + expect(doc.css('li.inapplicable input[data-inapplicable]').count).to eq(0) + end + end + + it_behaves_like 'a valid inapplicable task list item', '[~] foobar' + it_behaves_like 'a valid inapplicable task list item', '[~] foobar' + + it_behaves_like 'an invalid inapplicable task list item', '[ ] foobar' + it_behaves_like 'an invalid inapplicable task list item', '[x] foobar' + it_behaves_like 'an invalid inapplicable task list item', 'foo [~] bar' + it_behaves_like 'an invalid inapplicable task list item', 'foo [~] bar' + end end diff --git a/spec/support/matchers/markdown_matchers.rb b/spec/support/matchers/markdown_matchers.rb index 1932f78506fa0b..7cde868f4ecd86 100644 --- a/spec/support/matchers/markdown_matchers.rb +++ b/spec/support/matchers/markdown_matchers.rb @@ -189,8 +189,10 @@ def have_image(src) match do |actual| expect(actual).to have_selector('ul.task-list', count: 2) - expect(actual).to have_selector('li.task-list-item', count: 7) + expect(actual).to have_selector('li.task-list-item', count: 9) + expect(actual).to have_selector('li.task-list-item.inapplicable', count: 2) expect(actual).to have_selector('input[checked]', count: 3) + expect(actual).to have_selector('input[data-inapplicable]', count: 2) end end -- GitLab From 3e9ccb41b46ff16daf66ecbb9abb8e20af20757e Mon Sep 17 00:00:00 2001 From: Brett Walker Date: Wed, 29 Jun 2022 16:59:58 -0500 Subject: [PATCH 06/16] Add support for `[~]` in auto continue lists --- app/assets/javascripts/lib/utils/text_markdown.js | 4 ++-- app/assets/stylesheets/framework/typography.scss | 3 --- spec/frontend/lib/utils/text_markdown_spec.js | 4 ++++ 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/lib/utils/text_markdown.js b/app/assets/javascripts/lib/utils/text_markdown.js index 243de48948c43e..262cf024ee3d99 100644 --- a/app/assets/javascripts/lib/utils/text_markdown.js +++ b/app/assets/javascripts/lib/utils/text_markdown.js @@ -9,7 +9,7 @@ const LINK_TAG_PATTERN = '[{text}](url)'; // a bullet point character (*+-) and an optional checkbox ([ ] [x]) // OR a number with a . after it and an optional checkbox ([ ] [x]) // followed by one or more whitespace characters -const LIST_LINE_HEAD_PATTERN = /^(?\s*)(?((?[*+-])|(?\d+\.))( \[([xX\s])\])?\s)(?.)?/; +const LIST_LINE_HEAD_PATTERN = /^(?\s*)(?((?[*+-])|(?\d+\.))( \[([xX~\s])\])?\s)(?.)?/; // detect a horizontal rule that might be mistaken for a list item (not full pattern for an
    ) const HR_PATTERN = /^((\s{0,3}-+\s*-+\s*-+\s*[\s-]*)|(\s{0,3}\*+\s*\*+\s*\*+\s*[\s*]*))$/; @@ -399,7 +399,7 @@ function handleContinueList(e, textArea) { itemToInsert = `${indent}${leader}`; } - itemToInsert = itemToInsert.replace(/\[x\]/i, '[ ]'); + itemToInsert = itemToInsert.replace(/\[[x~]\]/i, '[ ]'); e.preventDefault(); diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index a1b625f1bd2429..75056ef99c9c16 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -436,16 +436,13 @@ } li.inapplicable { - text-decoration: line-through; color: $gl-text-color-disabled; ~ li:not(.inapplicable) { - text-decoration: none !important; color: $gl-text-color; } > p { - text-decoration: line-through; color: $gl-text-color-disabled; } diff --git a/spec/frontend/lib/utils/text_markdown_spec.js b/spec/frontend/lib/utils/text_markdown_spec.js index d1bca3c73b6438..8f37c79e2354f6 100644 --- a/spec/frontend/lib/utils/text_markdown_spec.js +++ b/spec/frontend/lib/utils/text_markdown_spec.js @@ -193,6 +193,7 @@ describe('init markdown', () => { ${'- [ ] item'} | ${'- [ ] item\n- [ ] '} ${'- [x] item'} | ${'- [x] item\n- [ ] '} ${'- [X] item'} | ${'- [X] item\n- [ ] '} + ${'- [~] item'} | ${'- [~] item\n- [ ] '} ${'- [ ] nbsp (U+00A0)'} | ${'- [ ] nbsp (U+00A0)\n- [ ] '} ${'- item\n - second'} | ${'- item\n - second\n - '} ${'- - -'} | ${'- - -'} @@ -205,6 +206,7 @@ describe('init markdown', () => { ${'1. [ ] item'} | ${'1. [ ] item\n2. [ ] '} ${'1. [x] item'} | ${'1. [x] item\n2. [ ] '} ${'1. [X] item'} | ${'1. [X] item\n2. [ ] '} + ${'1. [~] item'} | ${'1. [~] item\n2. [ ] '} ${'108. item'} | ${'108. item\n109. '} ${'108. item\n - second'} | ${'108. item\n - second\n - '} ${'108. item\n 1. second'} | ${'108. item\n 1. second\n 2. '} @@ -228,11 +230,13 @@ describe('init markdown', () => { ${'- [ ] item\n- [ ] '} | ${'- [ ] item\n'} ${'- [x] item\n- [x] '} | ${'- [x] item\n'} ${'- [X] item\n- [X] '} | ${'- [X] item\n'} + ${'- [~] item\n- [~] '} | ${'- [~] item\n'} ${'- item\n - second\n - '} | ${'- item\n - second\n'} ${'1. item\n2. '} | ${'1. item\n'} ${'1. [ ] item\n2. [ ] '} | ${'1. [ ] item\n'} ${'1. [x] item\n2. [x] '} | ${'1. [x] item\n'} ${'1. [X] item\n2. [X] '} | ${'1. [X] item\n'} + ${'1. [~] item\n2. [~] '} | ${'1. [~] item\n'} ${'108. item\n109. '} | ${'108. item\n'} ${'108. item\n - second\n - '} | ${'108. item\n - second\n'} ${'108. item\n 1. second\n 1. '} | ${'108. item\n 1. second\n'} -- GitLab From 6bfa2055bf40d5e9eeff805d44be179b8a542126 Mon Sep 17 00:00:00 2001 From: Brett Walker Date: Fri, 1 Jul 2022 12:55:22 -0500 Subject: [PATCH 07/16] Add a span around the initial text of inapplicable list item --- .../stylesheets/framework/typography.scss | 23 +++++++++++--- lib/banzai/filter/task_list_filter.rb | 31 ++++++++++++++++--- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index 75056ef99c9c16..aa7f4a3b1f06e9 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -436,13 +436,28 @@ } li.inapplicable { - color: $gl-text-color-disabled; + // for a single line list item, no paragraph (tight list) + > span { + text-decoration: line-through; + color: $gl-text-color-disabled; + } - ~ li:not(.inapplicable) { - color: $gl-text-color; + // additional blocks, other than paragraphs + > div { + text-decoration: line-through; + color: $gl-text-color-disabled; + } + + // because of the embedded checkbox, putting line-through on the entire + // paragraph causes the space between the checkbox and the text to have the + // line-through. Targeting just the span fixes this + > p:first-of-type span { + text-decoration: line-through; + color: $gl-text-color-disabled; } - > p { + > p:not(:first-of-type) { + text-decoration: line-through; color: $gl-text-color-disabled; } diff --git a/lib/banzai/filter/task_list_filter.rb b/lib/banzai/filter/task_list_filter.rb index 0991f03630b25a..0e3a39017f0131 100644 --- a/lib/banzai/filter/task_list_filter.rb +++ b/lib/banzai/filter/task_list_filter.rb @@ -28,6 +28,8 @@ module Filter # This class overrides TaskList::Filter in the `deckar01-task_list` gem # to add support for inapplicable task items class TaskListFilter < TaskList::Filter + extend ::Gitlab::Utils::Override + XPATH = 'descendant-or-self::li[input[@data-inapplicable]] | descendant-or-self::li[p[input[@data-inapplicable]]]' INAPPLICABLE = '[~]' INAPPLICABLEPATTERN = /\[~\]/.freeze @@ -54,6 +56,7 @@ def inapplicable?(item) !!(item.checkbox_text =~ INAPPLICABLEPATTERN) end + override :render_item_checkbox def render_item_checkbox(item) %() end + override :render_task_list_item + def render_task_list_item(item) + source = item.source + + if inapplicable?(item) + # Add a span tag around the list item text. However because of the + # way tasks are built, the source can include an embedded sublist, like + # `[~] foobar\n
      Date: Fri, 1 Jul 2022 15:41:46 -0500 Subject: [PATCH 08/16] Use a del tag instead of a span tag --- app/assets/stylesheets/framework/typography.scss | 8 +++----- lib/banzai/filter/task_list_filter.rb | 2 +- spec/lib/banzai/filter/task_list_filter_spec.rb | 14 +++++++++++--- spec/support/matchers/markdown_matchers.rb | 2 +- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index aa7f4a3b1f06e9..687d6d1058d308 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -437,8 +437,7 @@ li.inapplicable { // for a single line list item, no paragraph (tight list) - > span { - text-decoration: line-through; + > del { color: $gl-text-color-disabled; } @@ -450,9 +449,8 @@ // because of the embedded checkbox, putting line-through on the entire // paragraph causes the space between the checkbox and the text to have the - // line-through. Targeting just the span fixes this - > p:first-of-type span { - text-decoration: line-through; + // line-through. Targeting just the del fixes this + > p:first-of-type del { color: $gl-text-color-disabled; } diff --git a/lib/banzai/filter/task_list_filter.rb b/lib/banzai/filter/task_list_filter.rb index 0e3a39017f0131..6d143782336d46 100644 --- a/lib/banzai/filter/task_list_filter.rb +++ b/lib/banzai/filter/task_list_filter.rb @@ -76,7 +76,7 @@ def render_task_list_item(item) # The span should only be added to the main text. source = source.partition("#{INAPPLICABLE} ") text = source.last.partition(/\<(ol|ul)/) - text[0] = "#{text[0]}" + text[0] = "#{text[0]}" source[-1] = text.join source = source.join end diff --git a/spec/lib/banzai/filter/task_list_filter_spec.rb b/spec/lib/banzai/filter/task_list_filter_spec.rb index 74c77b6aa859e6..b03fb9d923ccef 100644 --- a/spec/lib/banzai/filter/task_list_filter_spec.rb +++ b/spec/lib/banzai/filter/task_list_filter_spec.rb @@ -17,6 +17,7 @@ doc = filter("
      • #{html}
      ") expect(doc.css('li.inapplicable input[data-inapplicable]').count).to eq(1) + expect(doc.css('li.inapplicable del').count).to eq(1) end end @@ -29,11 +30,18 @@ end it_behaves_like 'a valid inapplicable task list item', '[~] foobar' - it_behaves_like 'a valid inapplicable task list item', '[~] foobar' - + it_behaves_like 'a valid inapplicable task list item', '[~] foo bar' it_behaves_like 'an invalid inapplicable task list item', '[ ] foobar' it_behaves_like 'an invalid inapplicable task list item', '[x] foobar' it_behaves_like 'an invalid inapplicable task list item', 'foo [~] bar' - it_behaves_like 'an invalid inapplicable task list item', 'foo [~] bar' + + it 'does not wrap a sublist with del' do + html = '[~] foo bar\n
      1. sublist
      ' + doc = filter("
      • #{html}
      ") + + expect(doc.to_html).to include('foo bar\n') + expect(doc.css('li.inapplicable input[data-inapplicable]').count).to eq(1) + expect(doc.css('li.inapplicable del').count).to eq(1) + end end end diff --git a/spec/support/matchers/markdown_matchers.rb b/spec/support/matchers/markdown_matchers.rb index 7cde868f4ecd86..d87a1bfaa23daf 100644 --- a/spec/support/matchers/markdown_matchers.rb +++ b/spec/support/matchers/markdown_matchers.rb @@ -190,7 +190,7 @@ def have_image(src) match do |actual| expect(actual).to have_selector('ul.task-list', count: 2) expect(actual).to have_selector('li.task-list-item', count: 9) - expect(actual).to have_selector('li.task-list-item.inapplicable', count: 2) + expect(actual).to have_selector('li.task-list-item.inapplicable > del', count: 2) expect(actual).to have_selector('input[checked]', count: 3) expect(actual).to have_selector('input[data-inapplicable]', count: 2) end -- GitLab From 9909c4d9f8b8fe7e28b4ea2379038de721175716 Mon Sep 17 00:00:00 2001 From: Marcin Sedlak-Jakubowski Date: Mon, 11 Jul 2022 02:26:00 +0000 Subject: [PATCH 09/16] Documentation update --- doc/user/markdown.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/user/markdown.md b/doc/user/markdown.md index 1a7afbfc72a679..d628ff40af96e3 100644 --- a/doc/user/markdown.md +++ b/doc/user/markdown.md @@ -386,9 +386,8 @@ You can add task lists anywhere Markdown is supported. - In all other places, you cannot select the boxes. You must edit the Markdown manually by adding or removing an `x` in the brackets. -Besides complete and incomplete, tasks can also be inapplicable. An -inapplicable checkbox cannot be modified; clicking on an inapplicable checkbox -in an issue, merge request, or comment will have no effect. +Besides complete and incomplete, tasks can also be **inapplicable**. Selecting an inapplicable checkbox +in an issue, merge request, or comment has no effect. To create a task list, follow the format of an ordered or unordered list: -- GitLab From d09d8ba46a0c0c0d410c60960a0a4f30e4265c04 Mon Sep 17 00:00:00 2001 From: Brett Walker Date: Fri, 22 Jul 2022 10:08:51 -0500 Subject: [PATCH 10/16] Add minimal GLFM example snapshots - Adds new tasks lists section to GLFM specification with minimal examples for incomplete, completed, inapplicable, and loose-list inapplicable tasks. --- .../example_snapshots/examples_index.yml | 12 +++ .../example_snapshots/prosemirror_json.yml | 70 ++++++++++++++++ .../glfm_canonical_examples.txt | 82 +++++++++++++++++++ .../glfm_example_status.yml | 12 +++ glfm_specification/output/spec.txt | 81 ++++++++++++++++++ 5 files changed, 257 insertions(+) diff --git a/glfm_specification/example_snapshots/examples_index.yml b/glfm_specification/example_snapshots/examples_index.yml index 9b601460b1d913..a6668173cc9b2a 100644 --- a/glfm_specification/example_snapshots/examples_index.yml +++ b/glfm_specification/example_snapshots/examples_index.yml @@ -2015,3 +2015,15 @@ 07_01__gitlab_specific_markdown__footnotes__001: spec_txt_example_position: 674 source_specification: gitlab +07_02__gitlab_specific_markdown__task_list_items__001: + spec_txt_example_position: 675 + source_specification: gitlab +07_02__gitlab_specific_markdown__task_list_items__002: + spec_txt_example_position: 676 + source_specification: gitlab +07_02__gitlab_specific_markdown__task_list_items__003: + spec_txt_example_position: 677 + source_specification: gitlab +07_02__gitlab_specific_markdown__task_list_items__004: + spec_txt_example_position: 678 + source_specification: gitlab diff --git a/glfm_specification/example_snapshots/prosemirror_json.yml b/glfm_specification/example_snapshots/prosemirror_json.yml index 0b46894504238c..f770d341c426e8 100644 --- a/glfm_specification/example_snapshots/prosemirror_json.yml +++ b/glfm_specification/example_snapshots/prosemirror_json.yml @@ -19244,3 +19244,73 @@ } ] } +07_02__gitlab_specific_markdown__task_list_items__001: |- + { + "type": "doc", + "content": [ + { + "type": "taskList", + "attrs": { + "numeric": false, + "start": 1, + "parens": false + }, + "content": [ + { + "type": "taskItem", + "attrs": { + "checked": false + }, + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "incomplete" + } + ] + } + ] + } + ] + } + ] + } +07_02__gitlab_specific_markdown__task_list_items__002: |- + { + "type": "doc", + "content": [ + { + "type": "taskList", + "attrs": { + "numeric": false, + "start": 1, + "parens": false + }, + "content": [ + { + "type": "taskItem", + "attrs": { + "checked": true + }, + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "completed" + } + ] + } + ] + } + ] + } + ] + } +07_02__gitlab_specific_markdown__task_list_items__003: |- + Inapplicable task list items not yet implemented for WYSYWIG +07_02__gitlab_specific_markdown__task_list_items__004: |- + Inapplicable task list items not yet implemented for WYSYWIG diff --git a/glfm_specification/input/gitlab_flavored_markdown/glfm_canonical_examples.txt b/glfm_specification/input/gitlab_flavored_markdown/glfm_canonical_examples.txt index d0d450b66bf105..b7c3cd7c4a36c3 100644 --- a/glfm_specification/input/gitlab_flavored_markdown/glfm_canonical_examples.txt +++ b/glfm_specification/input/gitlab_flavored_markdown/glfm_canonical_examples.txt @@ -38,3 +38,85 @@ footnote text
    ```````````````````````````````` + +## Task list items + +See +[the Task lists section of the user-facing documentation for GitLab Flavored Markdown](https://docs.gitlab.com/ee/user/markdown.html#task-lists). + +Task list items (checkboxes) are defined as a GitHub Flavored Markdown extension in a section above. +However, GitLab extends the behavior of task list items to support additional features. +Some of these features are in-progress, and should not yet be considered part of the official +GitLab Flavored Markdown specification. + +Some of the behavior of task list items is implemented as client-side JavaScript/CSS. + +The following are some basic examples; more examples may be added in the future. + +Incomplete task: + +```````````````````````````````` example gitlab tasklist +- [ ] incomplete +. +
      +
    • + + +incomplete +
    • +
    +```````````````````````````````` + +Completed task: + +```````````````````````````````` example gitlab tasklist +- [x] completed +. +
      +
    • + + +completed +
    • +
    +```````````````````````````````` + +Inapplicable task: + +```````````````````````````````` example gitlab tasklist +- [~] inapplicable +. +
      +
    • + + + +inapplicable + +
    • +
    +```````````````````````````````` + +Inapplicable task in a "loose" list. Note that the `` tag is not applied to the +loose text; is has strikethrough applied through CSS. + +```````````````````````````````` example gitlab tasklist +- [~] inapplicable + + text in loose list +. +
      +
    • +

      + + + +inapplicable + +

      +

      +text in loose list +

      +
    • +
    +```````````````````````````````` diff --git a/glfm_specification/input/gitlab_flavored_markdown/glfm_example_status.yml b/glfm_specification/input/gitlab_flavored_markdown/glfm_example_status.yml index b09a092c02a503..3881819e38ae58 100644 --- a/glfm_specification/input/gitlab_flavored_markdown/glfm_example_status.yml +++ b/glfm_specification/input/gitlab_flavored_markdown/glfm_example_status.yml @@ -12,3 +12,15 @@ skip_running_snapshot_static_html_tests: false # NOT YET SUPPORTED skip_running_snapshot_wysiwyg_html_tests: false skip_running_snapshot_prosemirror_json_tests: false +07_02__gitlab_specific_markdown__task_list_items__003: + skip_update_example_snapshot_html_wysiwyg: Inapplicable task list items not yet implemented for WYSYWIG + skip_update_example_snapshot_prosemirror_json: Inapplicable task list items not yet implemented for WYSYWIG + skip_running_conformance_wysiwyg_tests: Inapplicable task list items not yet implemented for WYSYWIG + skip_running_snapshot_wysiwyg_html_tests: Inapplicable task list items not yet implemented for WYSYWIG + skip_running_snapshot_prosemirror_json_tests: Inapplicable task list items not yet implemented for WYSYWIG +07_02__gitlab_specific_markdown__task_list_items__004: + skip_update_example_snapshot_html_wysiwyg: Inapplicable task list items not yet implemented for WYSYWIG + skip_update_example_snapshot_prosemirror_json: Inapplicable task list items not yet implemented for WYSYWIG + skip_running_conformance_wysiwyg_tests: Inapplicable task list items not yet implemented for WYSYWIG + skip_running_snapshot_wysiwyg_html_tests: Inapplicable task list items not yet implemented for WYSYWIG + skip_running_snapshot_prosemirror_json_tests: Inapplicable task list items not yet implemented for WYSYWIG diff --git a/glfm_specification/output/spec.txt b/glfm_specification/output/spec.txt index 3fc27efdc34444..e2ceede5b8a5e7 100644 --- a/glfm_specification/output/spec.txt +++ b/glfm_specification/output/spec.txt @@ -9641,6 +9641,87 @@ footnote text ```````````````````````````````` +## Task list items + +See +[the Task lists section of the user-facing documentation for GitLab Flavored Markdown](https://docs.gitlab.com/ee/user/markdown.html#task-lists). + +Task list items (checkboxes) are defined as a GitHub Flavored Markdown extension in a section above. +However, GitLab extends the behavior of task list items to support additional features. +Some of these features are in-progress, and should not yet be considered part of the official +GitLab Flavored Markdown specification. + +Some of the behavior of task list items is implemented as client-side JavaScript. + +The following are some basic examples; more examples may be added in the future. + +Incomplete task: + +```````````````````````````````` example gitlab tasklist +- [ ] incomplete +. +
      +
    • + + +incomplete +
    • +
    +```````````````````````````````` + +Completed task: + +```````````````````````````````` example gitlab tasklist +- [x] completed +. +
      +
    • + + +completed +
    • +
    +```````````````````````````````` + +Inapplicable task: + +```````````````````````````````` example gitlab tasklist +- [~] inapplicable +. +
      +
    • + + + +inapplicable + +
    • +
    +```````````````````````````````` + +Inapplicable task in a "loose" list: + +```````````````````````````````` example gitlab tasklist +- [~] inapplicable + + text in loose list +. +
      +
    • +

      + + + +inapplicable + +

      +

      +text in loose list +

      +
    • +
    +```````````````````````````````` + # Appendix: A parsing strategy -- GitLab From 1ee0612cbe7c8c0d8f4a6e6872ecc1ff06b82cbf Mon Sep 17 00:00:00 2001 From: Brett Walker Date: Tue, 19 Jul 2022 10:17:17 -0500 Subject: [PATCH 11/16] Minor review fixes --- app/assets/javascripts/task_list.js | 2 +- lib/banzai/filter/task_list_filter.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/task_list.js b/app/assets/javascripts/task_list.js index 6b6c16284531da..6e72d95c8e653e 100644 --- a/app/assets/javascripts/task_list.js +++ b/app/assets/javascripts/task_list.js @@ -76,7 +76,7 @@ export default class TaskList { enableTaskListItems(e) { this.getTaskListTarget(e).taskList('enable'); this.disableNonMarkdownTaskListItems(e); - this.updateInapplicableTaskListItems(); + this.updateInapplicableTaskListItems(e); } enable() { diff --git a/lib/banzai/filter/task_list_filter.rb b/lib/banzai/filter/task_list_filter.rb index 6d143782336d46..ffbd04ab1171b5 100644 --- a/lib/banzai/filter/task_list_filter.rb +++ b/lib/banzai/filter/task_list_filter.rb @@ -8,8 +8,8 @@ # - app/assets/javascripts/behaviors/markdown/nodes/task_list_item.js module Banzai module Filter - # TaskList filter replaces task list item markers (`[ ]` and `[x]`) with - # checkboxes, marked up with metadata and behavior. + # TaskList filter replaces task list item markers (`[ ]`, `[x]`, and `[~]`) + # with checkboxes, marked up with metadata and behavior. # # This should be run on the HTML generated by the Markdown filter, after the # SanitizationFilter. -- GitLab From bfbc1ddfba4f5988a7400c70099e23884716a929 Mon Sep 17 00:00:00 2001 From: Brett Walker Date: Tue, 19 Jul 2022 10:18:49 -0500 Subject: [PATCH 12/16] Recognize inapplicable list item during GFM paste --- .../behaviors/markdown/marks/strike.js | 19 ++++++++++++++----- spec/features/markdown/copy_as_gfm_spec.rb | 14 +++++++++++++- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/behaviors/markdown/marks/strike.js b/app/assets/javascripts/behaviors/markdown/marks/strike.js index 967c0a120cd9e8..4ef04ea6358096 100644 --- a/app/assets/javascripts/behaviors/markdown/marks/strike.js +++ b/app/assets/javascripts/behaviors/markdown/marks/strike.js @@ -2,16 +2,25 @@ export default () => ({ name: 'strike', schema: { - parseDOM: [ - { - tag: 'del', + attrs: { + inapplicable: { + default: false, }, + }, + parseDOM: [ + { tag: 'li.inapplicable > del', attrs: { inapplicable: true } }, + { tag: 'li.inapplicable > p:first-of-type > del', attrs: { inapplicable: true } }, + { tag: 'del'}, ], toDOM: () => ['s', 0], }, toMarkdown: { - open: '~~', - close: '~~', + open(_, mark) { + return mark.attrs.inapplicable ? '' : '~~'; + }, + close(_, mark) { + return mark.attrs.inapplicable ? '' : '~~'; + }, mixable: true, expelEnclosingWhitespace: true, }, diff --git a/spec/features/markdown/copy_as_gfm_spec.rb b/spec/features/markdown/copy_as_gfm_spec.rb index 71f488a43e3d1e..5bcc832911c9b2 100644 --- a/spec/features/markdown/copy_as_gfm_spec.rb +++ b/spec/features/markdown/copy_as_gfm_spec.rb @@ -110,11 +110,23 @@ * [ ] Unchecked task * [x] Checked task * [~] Inapplicable task + * [~] Inapplicable task with ~~del~~ embedded GFM - <<~GFM + <<~GFM, 1. [ ] Unchecked ordered task 1. [x] Checked ordered task 1. [~] Inapplicable ordered task + 1. [~] Inapplicable ordered task with ~~del~~ embedded + GFM + <<~GFM + * [ ] Unchecked loose list task + * [x] Checked loose list task + * [~] Inapplicable loose list task + + With a paragraph + * [~] Inapplicable loose list task with ~~del~~ embedded + + With a paragraph GFM ) -- GitLab From e7e9f38080c6cb1879aa4774d6ad7c1d5e97a078 Mon Sep 17 00:00:00 2001 From: Brett Walker Date: Tue, 19 Jul 2022 12:28:55 -0500 Subject: [PATCH 13/16] Use instead of for inapplicable lists for better semantic meaning --- .../behaviors/markdown/marks/strike.js | 16 +++++++++++++--- app/assets/stylesheets/framework/typography.scss | 6 +++--- lib/banzai/filter/task_list_filter.rb | 6 +++--- spec/features/markdown/copy_as_gfm_spec.rb | 9 +++++---- spec/lib/banzai/filter/task_list_filter_spec.rb | 8 ++++---- spec/support/matchers/markdown_matchers.rb | 2 +- 6 files changed, 29 insertions(+), 18 deletions(-) diff --git a/app/assets/javascripts/behaviors/markdown/marks/strike.js b/app/assets/javascripts/behaviors/markdown/marks/strike.js index 4ef04ea6358096..afab266b645654 100644 --- a/app/assets/javascripts/behaviors/markdown/marks/strike.js +++ b/app/assets/javascripts/behaviors/markdown/marks/strike.js @@ -3,22 +3,32 @@ export default () => ({ name: 'strike', schema: { attrs: { + strike: { + default: false, + }, inapplicable: { default: false, }, }, parseDOM: [ - { tag: 'li.inapplicable > del', attrs: { inapplicable: true } }, - { tag: 'li.inapplicable > p:first-of-type > del', attrs: { inapplicable: true } }, - { tag: 'del'}, + { tag: 'li.inapplicable > s', attrs: { inapplicable: true } }, + { tag: 'li.inapplicable > p:first-of-type > s', attrs: { inapplicable: true } }, + { tag: 's', attrs: { strike: true } }, + { tag: 'del' }, ], toDOM: () => ['s', 0], }, toMarkdown: { open(_, mark) { + if (mark.attrs.strike) { + return ''; + } return mark.attrs.inapplicable ? '' : '~~'; }, close(_, mark) { + if (mark.attrs.strike) { + return ''; + } return mark.attrs.inapplicable ? '' : '~~'; }, mixable: true, diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index 687d6d1058d308..462b2847455f10 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -437,7 +437,7 @@ li.inapplicable { // for a single line list item, no paragraph (tight list) - > del { + > s { color: $gl-text-color-disabled; } @@ -449,8 +449,8 @@ // because of the embedded checkbox, putting line-through on the entire // paragraph causes the space between the checkbox and the text to have the - // line-through. Targeting just the del fixes this - > p:first-of-type del { + // line-through. Targeting just the `s` fixes this + > p:first-of-type > s { color: $gl-text-color-disabled; } diff --git a/lib/banzai/filter/task_list_filter.rb b/lib/banzai/filter/task_list_filter.rb index ffbd04ab1171b5..e8a7677b102490 100644 --- a/lib/banzai/filter/task_list_filter.rb +++ b/lib/banzai/filter/task_list_filter.rb @@ -70,13 +70,13 @@ def render_task_list_item(item) source = item.source if inapplicable?(item) - # Add a span tag around the list item text. However because of the + # Add a `` tag around the list item text. However because of the # way tasks are built, the source can include an embedded sublist, like # `[~] foobar\n
      ` should only be added to the main text. source = source.partition("#{INAPPLICABLE} ") text = source.last.partition(/\<(ol|ul)/) - text[0] = "#{text[0]}" + text[0] = "#{text[0]}" source[-1] = text.join source = source.join end diff --git a/spec/features/markdown/copy_as_gfm_spec.rb b/spec/features/markdown/copy_as_gfm_spec.rb index 5bcc832911c9b2..b5bf9279371547 100644 --- a/spec/features/markdown/copy_as_gfm_spec.rb +++ b/spec/features/markdown/copy_as_gfm_spec.rb @@ -110,13 +110,13 @@ * [ ] Unchecked task * [x] Checked task * [~] Inapplicable task - * [~] Inapplicable task with ~~del~~ embedded + * [~] Inapplicable task with ~~del~~ and strike embedded GFM <<~GFM, 1. [ ] Unchecked ordered task 1. [x] Checked ordered task 1. [~] Inapplicable ordered task - 1. [~] Inapplicable ordered task with ~~del~~ embedded + 1. [~] Inapplicable ordered task with ~~del~~ and strike embedded GFM <<~GFM * [ ] Unchecked loose list task @@ -124,7 +124,7 @@ * [~] Inapplicable loose list task With a paragraph - * [~] Inapplicable loose list task with ~~del~~ embedded + * [~] Inapplicable loose list task with ~~del~~ and strike embedded With a paragraph GFM @@ -619,7 +619,8 @@ def foo '###### Heading', '**Bold**', '*Italics*', - '~~Strikethrough~~', + '~~Strikethrough (del)~~', + 'Strikethrough', '---', # table <<~GFM, diff --git a/spec/lib/banzai/filter/task_list_filter_spec.rb b/spec/lib/banzai/filter/task_list_filter_spec.rb index b03fb9d923ccef..920904b0f29b77 100644 --- a/spec/lib/banzai/filter/task_list_filter_spec.rb +++ b/spec/lib/banzai/filter/task_list_filter_spec.rb @@ -17,7 +17,7 @@ doc = filter("
      • #{html}
      ") expect(doc.css('li.inapplicable input[data-inapplicable]').count).to eq(1) - expect(doc.css('li.inapplicable del').count).to eq(1) + expect(doc.css('li.inapplicable > s').count).to eq(1) end end @@ -35,13 +35,13 @@ it_behaves_like 'an invalid inapplicable task list item', '[x] foobar' it_behaves_like 'an invalid inapplicable task list item', 'foo [~] bar' - it 'does not wrap a sublist with del' do + it 'does not wrap a sublist with ' do html = '[~] foo bar\n
      1. sublist
      ' doc = filter("
      • #{html}
      ") - expect(doc.to_html).to include('foo bar\n') + expect(doc.to_html).to include('foo bar\n') expect(doc.css('li.inapplicable input[data-inapplicable]').count).to eq(1) - expect(doc.css('li.inapplicable del').count).to eq(1) + expect(doc.css('li.inapplicable > s').count).to eq(1) end end end diff --git a/spec/support/matchers/markdown_matchers.rb b/spec/support/matchers/markdown_matchers.rb index d87a1bfaa23daf..8bec3be2535fe9 100644 --- a/spec/support/matchers/markdown_matchers.rb +++ b/spec/support/matchers/markdown_matchers.rb @@ -190,7 +190,7 @@ def have_image(src) match do |actual| expect(actual).to have_selector('ul.task-list', count: 2) expect(actual).to have_selector('li.task-list-item', count: 9) - expect(actual).to have_selector('li.task-list-item.inapplicable > del', count: 2) + expect(actual).to have_selector('li.task-list-item.inapplicable > s', count: 2) expect(actual).to have_selector('input[checked]', count: 3) expect(actual).to have_selector('input[data-inapplicable]', count: 2) end -- GitLab From c1aa54a45b6aeb8256497d07c3a36a06f61697c8 Mon Sep 17 00:00:00 2001 From: Brett Walker Date: Fri, 22 Jul 2022 10:09:26 -0500 Subject: [PATCH 14/16] Update snapshots to use --- .../glfm_canonical_examples.txt | 12 ++++++------ glfm_specification/output/spec.txt | 17 +++++++++-------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/glfm_specification/input/gitlab_flavored_markdown/glfm_canonical_examples.txt b/glfm_specification/input/gitlab_flavored_markdown/glfm_canonical_examples.txt index b7c3cd7c4a36c3..4853d9305f79df 100644 --- a/glfm_specification/input/gitlab_flavored_markdown/glfm_canonical_examples.txt +++ b/glfm_specification/input/gitlab_flavored_markdown/glfm_canonical_examples.txt @@ -42,10 +42,10 @@ footnote text ## Task list items See -[the Task lists section of the user-facing documentation for GitLab Flavored Markdown](https://docs.gitlab.com/ee/user/markdown.html#task-lists). +[Task lists](https://docs.gitlab.com/ee/user/markdown.html#task-lists) in the GitLab Flavored Markdown documentation. Task list items (checkboxes) are defined as a GitHub Flavored Markdown extension in a section above. -However, GitLab extends the behavior of task list items to support additional features. +GitLab extends the behavior of task list items to support additional features. Some of these features are in-progress, and should not yet be considered part of the official GitLab Flavored Markdown specification. @@ -90,9 +90,9 @@ Inapplicable task:
    1. - + inapplicable - +
```````````````````````````````` @@ -110,9 +110,9 @@ loose text; is has strikethrough applied through CSS.

- + inapplicable - +

text in loose list diff --git a/glfm_specification/output/spec.txt b/glfm_specification/output/spec.txt index e2ceede5b8a5e7..52c5e4da23e3b5 100644 --- a/glfm_specification/output/spec.txt +++ b/glfm_specification/output/spec.txt @@ -9644,14 +9644,14 @@ footnote text ## Task list items See -[the Task lists section of the user-facing documentation for GitLab Flavored Markdown](https://docs.gitlab.com/ee/user/markdown.html#task-lists). +[Task lists](https://docs.gitlab.com/ee/user/markdown.html#task-lists) in the GitLab Flavored Markdown documentation. Task list items (checkboxes) are defined as a GitHub Flavored Markdown extension in a section above. -However, GitLab extends the behavior of task list items to support additional features. +GitLab extends the behavior of task list items to support additional features. Some of these features are in-progress, and should not yet be considered part of the official GitLab Flavored Markdown specification. -Some of the behavior of task list items is implemented as client-side JavaScript. +Some of the behavior of task list items is implemented as client-side JavaScript/CSS. The following are some basic examples; more examples may be added in the future. @@ -9692,14 +9692,15 @@ Inapplicable task:

  • - + inapplicable - +
  • ```````````````````````````````` -Inapplicable task in a "loose" list: +Inapplicable task in a "loose" list. Note that the `` tag is not applied to the +loose text; is has strikethrough applied through CSS. ```````````````````````````````` example gitlab tasklist - [~] inapplicable @@ -9711,9 +9712,9 @@ Inapplicable task in a "loose" list:

    - + inapplicable - +

    text in loose list -- GitLab From ecd091d81a6b7a30eb3d6a4aa21c2e64760fd51b Mon Sep 17 00:00:00 2001 From: Brett Walker Date: Tue, 19 Jul 2022 16:24:52 -0500 Subject: [PATCH 15/16] Update task list image --- doc/user/img/completed_tasks_v15_2.png | Bin 57454 -> 0 bytes doc/user/img/completed_tasks_v15_3.png | Bin 0 -> 55931 bytes doc/user/markdown.md | 4 ++-- 3 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 doc/user/img/completed_tasks_v15_2.png create mode 100644 doc/user/img/completed_tasks_v15_3.png diff --git a/doc/user/img/completed_tasks_v15_2.png b/doc/user/img/completed_tasks_v15_2.png deleted file mode 100644 index 067b960c9097ed45f7d7cb8b52156e176efcef16..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57454 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%0DIeEoa6}C!X6;^r0Rv=-0 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