diff --git a/airflow/www/utils.py b/airflow/www/utils.py
index b0c93ba404ba8..e0ba0dbaadf3d 100644
--- a/airflow/www/utils.py
+++ b/airflow/www/utils.py
@@ -16,6 +16,7 @@
# specific language governing permissions and limitations
# under the License.
import json
+import textwrap
import time
from urllib.parse import urlencode
@@ -344,9 +345,7 @@ def wrapped_markdown(s, css_class='rich_doc'):
"""Convert a Markdown string to HTML."""
if s is None:
return None
-
- s = '\n'.join(line.lstrip() for line in s.split('\n'))
-
+ s = textwrap.dedent(s)
return Markup(f'
' + markdown.markdown(s, extensions=['tables']) + "
")
diff --git a/tests/www/test_utils.py b/tests/www/test_utils.py
index 8f381a54b8dfa..01c49e1fdcafd 100644
--- a/tests/www/test_utils.py
+++ b/tests/www/test_utils.py
@@ -218,3 +218,39 @@ def test_wrapped_markdown_with_indented_lines(self):
)
assert 'header
\n
1st line\n2nd line
' == rendered
+
+ def test_wrapped_markdown_with_raw_code_block(self):
+ rendered = wrapped_markdown(
+ """\
+ # Markdown code block
+
+ Inline `code` works well.
+
+ Code block
+ does not
+ respect
+ newlines
+
+ """
+ )
+
+ assert (
+ 'Markdown code block
\n'
+ '
Inline code
works well.
\n'
+ '
Code block\ndoes not\nrespect\nnewlines\n
'
+ ) == rendered
+
+ def test_wrapped_markdown_with_nested_list(self):
+ rendered = wrapped_markdown(
+ """
+ ### Docstring with a code block
+
+ - And
+ - A nested list
+ """
+ )
+
+ assert (
+ 'Docstring with a code block
\n'
+ '
'
+ ) == rendered