diff --git a/app/models/concerns/taskable.rb b/app/models/concerns/taskable.rb index ebc75100a546c8e298fb8c206cceca7c9390b562..68385dc47eb2db4cf89c9ffa55097c7e3f077cc6 100644 --- a/app/models/concerns/taskable.rb +++ b/app/models/concerns/taskable.rb @@ -11,7 +11,7 @@ module Taskable INCOMPLETE = 'incomplete'.freeze ITEM_PATTERN = / ^ - (?:\s*[-+*]|(?:\d+\.))? # optional list prefix + \s*(?:[-+*]|(?:\d+\.))? # optional list prefix \s* # optional whitespace prefix (\[\s\]|\[[xX]\]) # checkbox (\s.+) # followed by whitespace and some text. diff --git a/changelogs/unreleased/24923_nested_tasks.yml b/changelogs/unreleased/24923_nested_tasks.yml new file mode 100644 index 0000000000000000000000000000000000000000..de35cad3dd69a8ba26a17667bd569f35b9ffe961 --- /dev/null +++ b/changelogs/unreleased/24923_nested_tasks.yml @@ -0,0 +1,4 @@ +--- +title: Fix nested tasks in ordered list +merge_request: 8626 +author: diff --git a/doc/user/markdown.md b/doc/user/markdown.md index f64846887219288d314ea6c4b3baea1d2dc2e947..008872b59a73420abdf0bcba983a190301b129b4 100644 --- a/doc/user/markdown.md +++ b/doc/user/markdown.md @@ -300,6 +300,20 @@ You can add task lists to issues, merge requests and comments. To create a task - [x] Sub-task 2 - [ ] Sub-task 3 +Tasks formatted as ordered lists are supported as well: + +```no-highlight +1. [x] Completed task +1. [ ] Incomplete task + 1. [ ] Sub-task 1 + 1. [x] Sub-task 2 +``` + +1. [x] Completed task +1. [ ] Incomplete task + 1. [ ] Sub-task 1 + 1. [x] Sub-task 2 + Task lists can only be created in descriptions, not in titles. Task item state can be managed by editing the description's Markdown or by toggling the rendered check boxes. ### Videos @@ -650,7 +664,7 @@ This line is separated from the one above by two newlines, so it will be a *sepa This line is also a separate paragraph, but... This line is only separated by a single newline, so it's a separate line in the *same paragraph*. -This line is also a separate paragraph, and... +This line is also a separate paragraph, and... This line is on its own line, because the previous line ends with two spaces. ``` @@ -662,7 +676,7 @@ This line is separated from the one above by two newlines, so it will be a *sepa This line is also begins a separate paragraph, but... This line is only separated by a single newline, so it's a separate line in the *same paragraph*. -This line is also a separate paragraph, and... +This line is also a separate paragraph, and... This line is on its own line, because the previous line ends with two spaces. @@ -800,4 +814,4 @@ A link starting with a `/` is relative to the wiki root. [redcarpet]: https://github.com/vmg/redcarpet "Redcarpet website" [katex]: https://github.com/Khan/KaTeX "KaTeX website" [katex-subset]: https://github.com/Khan/KaTeX/wiki/Function-Support-in-KaTeX "Macros supported by KaTeX" -[asciidoctor-manual]: http://asciidoctor.org/docs/user-manual/#activating-stem-support "Asciidoctor user manual" \ No newline at end of file +[asciidoctor-manual]: http://asciidoctor.org/docs/user-manual/#activating-stem-support "Asciidoctor user manual" diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb index abb27c90e0a969ec83590d5162079c886ad663e5..a5d14aa19f1606f7b0608b4ca4a189e927ef298f 100644 --- a/spec/features/task_lists_spec.rb +++ b/spec/features/task_lists_spec.rb @@ -36,6 +36,19 @@ feature 'Task Lists', feature: true do MARKDOWN end + let(:nested_tasks_markdown) do + <<-EOT.strip_heredoc + - [ ] Task a + - [x] Task a.1 + - [ ] Task a.2 + - [ ] Task b + + 1. [ ] Task 1 + 1. [ ] Task 1.1 + 1. [x] Task 1.2 + EOT + end + before do Warden.test_mode! @@ -123,6 +136,35 @@ feature 'Task Lists', feature: true do expect(page).to have_content("1 of 1 task completed") end end + + describe 'nested tasks', js: true do + let(:issue) { create(:issue, description: nested_tasks_markdown, author: user, project: project) } + + before { visit_issue(project, issue) } + + it 'renders' do + expect(page).to have_selector('ul.task-list', count: 2) + expect(page).to have_selector('li.task-list-item', count: 7) + expect(page).to have_selector('ul input[checked]', count: 1) + expect(page).to have_selector('ol input[checked]', count: 1) + end + + it 'solves tasks' do + expect(page).to have_content("2 of 7 tasks completed") + + page.find('li.task-list-item', text: 'Task b').find('input').click + page.find('li.task-list-item ul li.task-list-item', text: 'Task a.2').find('input').click + page.find('li.task-list-item ol li.task-list-item', text: 'Task 1.1').find('input').click + + expect(page).to have_content("5 of 7 tasks completed") + + visit_issue(project, issue) # reload to see new system notes + + expect(page).to have_content('marked the task Task b as complete') + expect(page).to have_content('marked the task Task a.2 as complete') + expect(page).to have_content('marked the task Task 1.1 as complete') + end + end end describe 'for Notes' do @@ -236,7 +278,7 @@ feature 'Task Lists', feature: true do expect(page).to have_content("2 of 6 tasks completed") end end - + describe 'single incomplete task' do let!(:merge) { create(:merge_request, :simple, description: singleIncompleteMarkdown, author: user, source_project: project) } diff --git a/spec/support/taskable_shared_examples.rb b/spec/support/taskable_shared_examples.rb index ad1c783df4d917fc9c2460d02cd7f71f0cc69654..1b6c33248c98f80f4f4826dc33e7398884d1329c 100644 --- a/spec/support/taskable_shared_examples.rb +++ b/spec/support/taskable_shared_examples.rb @@ -33,6 +33,30 @@ shared_examples 'a Taskable' do end end + describe 'with nested tasks' do + before do + subject.description = <<-EOT.strip_heredoc + - [ ] Task a + - [x] Task a.1 + - [ ] Task a.2 + - [ ] Task b + + 1. [ ] Task 1 + 1. [ ] Task 1.1 + 1. [ ] Task 1.2 + 1. [x] Task 2 + 1. [x] Task 2.1 + EOT + end + + it 'returns the correct task status' do + expect(subject.task_status).to match('3 of') + expect(subject.task_status).to match('9 tasks completed') + expect(subject.task_status_short).to match('3/') + expect(subject.task_status_short).to match('9 tasks') + end + end + describe 'with an incomplete task' do before do subject.description = <<-EOT.strip_heredoc