mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Use colspan on td instead of divs for hierarchical tables (#39948)
Address Firefox table-rendering issues in docs. Refactor to use colspan to provide table cells which can vary in width and indentation; the outermost has the greatest colspan, and each nested key has a colspan of one less than the parent, with padding cells for indentation. Apply styling to table cells to get the table height to work without hacks or browser-specific styling. Simplify the markup and CSS by removing extra divs. Use two passes over the options, return values, and return facts in the Jinja2 module-docs template: one to determine the maximum nesting depth to compute the maximum colspan needed, plus one to lay out the rows.
This commit is contained in:
parent
91fd98a2bd
commit
fa5c0282a4
3 changed files with 162 additions and 215 deletions
|
@ -421,51 +421,17 @@ table {
|
|||
max-width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.outer-elbow-container {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.elbow-placeholder {
|
||||
.documentation-table td.elbow-placeholder {
|
||||
border-left: 1px solid #000;
|
||||
height: 100%;
|
||||
border-top: 0px;
|
||||
width: 30px;
|
||||
min-width: 30px;
|
||||
}
|
||||
|
||||
.elbow-key {
|
||||
height: 100%;
|
||||
padding: 4px;
|
||||
border-top: 1px solid #000;
|
||||
flex-grow: 1;
|
||||
border-left: 1px solid #000;
|
||||
}
|
||||
|
||||
.elbow-blocker {
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.return-value-column {
|
||||
height: 1px
|
||||
}
|
||||
|
||||
.return-value-column td {
|
||||
height: inherit
|
||||
}
|
||||
|
||||
@-moz-document url-prefix() {
|
||||
.return-value-column td {
|
||||
height: 100%
|
||||
}
|
||||
}
|
||||
|
||||
.cell-border {
|
||||
.documentation-table th, .documentation-table td {
|
||||
padding: 4px;
|
||||
border-left: 1px solid #000;
|
||||
border-top: 1px solid #000;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.documentation-table {
|
||||
|
|
|
@ -4867,50 +4867,17 @@ table {
|
|||
}
|
||||
}
|
||||
|
||||
.outer-elbow-container {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.elbow-placeholder {
|
||||
.documentation-table td.elbow-placeholder {
|
||||
border-left: 1px solid #000;
|
||||
height: 100%;
|
||||
border-top: 0px;
|
||||
width: 30px;
|
||||
min-width: 30px;
|
||||
}
|
||||
|
||||
.elbow-key {
|
||||
height: 100%;
|
||||
padding: 4px;
|
||||
border-top: 1px solid #000;
|
||||
flex-grow: 1;
|
||||
border-left: 1px solid #000;
|
||||
}
|
||||
|
||||
.elbow-blocker {
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.return-value-column {
|
||||
height: 1px
|
||||
}
|
||||
|
||||
.return-value-column td {
|
||||
height: inherit
|
||||
}
|
||||
|
||||
@-moz-document url-prefix() {
|
||||
.return-value-column td {
|
||||
height: 100%
|
||||
}
|
||||
}
|
||||
|
||||
.cell-border {
|
||||
.documentation-table th, .documentation-table td {
|
||||
padding: 4px;
|
||||
border-left: 1px solid #000;
|
||||
border-top: 1px solid #000;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.documentation-table {
|
||||
|
|
294
docs/templates/plugin.rst.j2
vendored
294
docs/templates/plugin.rst.j2
vendored
|
@ -88,104 +88,107 @@ Parameters
|
|||
.. raw:: html
|
||||
|
||||
<table border=0 cellpadding=0 class="documentation-table">
|
||||
{# Pre-compute the nesting depth to allocate columns #}
|
||||
{% set ns = namespace(maxdepth=1) %}
|
||||
{% for key, value in options|dictsort recursive %}
|
||||
{% set ns.maxdepth = [loop.depth, ns.maxdepth] | max %}
|
||||
{% if value.suboptions %}
|
||||
{% if value.suboptions.items %}
|
||||
@{ loop(value.suboptions.items()) }@
|
||||
{% elif value.suboptions[0].items %}
|
||||
@{ loop(value.suboptions[0].items()) }@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{# Header of the documentation #}
|
||||
<tr>
|
||||
<th class="head"><div class="cell-border">Parameter</div></th>
|
||||
<th class="head"><div class="cell-border">Choices/<font color="blue">Defaults</font></div></th>
|
||||
<th colspan="@{ ns.maxdepth }@">Parameter</th>
|
||||
<th>Choices/<font color="blue">Defaults</font></th>
|
||||
{% if plugin_type != 'module' %}
|
||||
<th class="head"><div class="cell-border">Configuration</div></th>
|
||||
<th>Configuration</th>
|
||||
{% endif %}
|
||||
<th class="head" width="100%"><div class="cell-border">Comments</div></th>
|
||||
<th width="100%">Comments</th>
|
||||
</tr>
|
||||
{% for key, value in options|dictsort recursive %}
|
||||
<tr class="return-value-column">
|
||||
<tr>
|
||||
{# indentation based on nesting level #}
|
||||
{% for i in range(1, loop.depth) %}
|
||||
<td class="elbow-placeholder"></td>
|
||||
{% endfor %}
|
||||
{# parameter name with required and/or introduced label #}
|
||||
<td>
|
||||
<div class="outer-elbow-container">
|
||||
{% for i in range(1, loop.depth) %}
|
||||
<div class="elbow-placeholder"> </div>
|
||||
{% endfor %}
|
||||
<div class="elbow-key">
|
||||
<b>@{ key }@</b>
|
||||
{% if value.get('required', False) %}<br/><div style="font-size: small; color: red">required</div>{% endif %}
|
||||
{% if value.version_added %}<br/><div style="font-size: small; color: darkgreen">(added in @{value.version_added}@)</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<td colspan="@{ ns.maxdepth - loop.depth0 }@">
|
||||
<b>@{ key }@</b>
|
||||
{% if value.get('required', False) %}<br/><div style="font-size: small; color: red">required</div>{% endif %}
|
||||
{% if value.version_added %}<br/><div style="font-size: small; color: darkgreen">(added in @{value.version_added}@)</div>{% endif %}
|
||||
</td>
|
||||
{# default / choices #}
|
||||
<td>
|
||||
<div class="cell-border">
|
||||
{# Turn boolean values in 'yes' and 'no' values #}
|
||||
{% if value.default is sameas true %}
|
||||
{% set _x = value.update({'default': 'yes'}) %}
|
||||
{% elif value.default is sameas false %}
|
||||
{% set _x = value.update({'default': 'no'}) %}
|
||||
{% endif %}
|
||||
{% if value.type == 'bool' %}
|
||||
{% set _x = value.update({'choices': ['no', 'yes']}) %}
|
||||
{% endif %}
|
||||
{# Show possible choices and highlight details #}
|
||||
{% if value.choices %}
|
||||
<ul><b>Choices:</b>
|
||||
{% for choice in value.choices %}
|
||||
{# Turn boolean values in 'yes' and 'no' values #}
|
||||
{% if choice is sameas true %}
|
||||
{% set choice = 'yes' %}
|
||||
{% elif choice is sameas false %}
|
||||
{% set choice = 'no' %}
|
||||
{% endif %}
|
||||
{% if (value.default is string and value.default == choice) or (value.default is iterable and value.default is not string and choice in value.default) %}
|
||||
<li><div style="color: blue"><b>@{ choice | escape }@</b> ←</div></li>
|
||||
{% else %}
|
||||
<li>@{ choice | escape }@</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{# Show default value, when multiple choice or no choices #}
|
||||
{% if value.default is defined and value.default not in value.choices %}
|
||||
<b>Default:</b><br/><div style="color: blue">@{ value.default | escape }@</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{# Turn boolean values in 'yes' and 'no' values #}
|
||||
{% if value.default is sameas true %}
|
||||
{% set _x = value.update({'default': 'yes'}) %}
|
||||
{% elif value.default is sameas false %}
|
||||
{% set _x = value.update({'default': 'no'}) %}
|
||||
{% endif %}
|
||||
{% if value.type == 'bool' %}
|
||||
{% set _x = value.update({'choices': ['no', 'yes']}) %}
|
||||
{% endif %}
|
||||
{# Show possible choices and highlight details #}
|
||||
{% if value.choices %}
|
||||
<ul><b>Choices:</b>
|
||||
{% for choice in value.choices %}
|
||||
{# Turn boolean values in 'yes' and 'no' values #}
|
||||
{% if choice is sameas true %}
|
||||
{% set choice = 'yes' %}
|
||||
{% elif choice is sameas false %}
|
||||
{% set choice = 'no' %}
|
||||
{% endif %}
|
||||
{% if (value.default is string and value.default == choice) or (value.default is iterable and value.default is not string and choice in value.default) %}
|
||||
<li><div style="color: blue"><b>@{ choice | escape }@</b> ←</div></li>
|
||||
{% else %}
|
||||
<li>@{ choice | escape }@</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{# Show default value, when multiple choice or no choices #}
|
||||
{% if value.default is defined and value.default not in value.choices %}
|
||||
<b>Default:</b><br/><div style="color: blue">@{ value.default | escape }@</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
{# configuration #}
|
||||
{% if plugin_type != 'module' %}
|
||||
<td>
|
||||
<div class="cell-border">
|
||||
{% if 'ini' in value %}
|
||||
<div> ini entries:
|
||||
{% for ini in value.ini %}
|
||||
<p>[@{ ini.section }@ ]<br>@{ ini.key }@ = @{ value.default | default('VALUE') }@</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if 'env' in value %}
|
||||
{% for env in value.env %}
|
||||
<div>env:@{ env.name }@</div>
|
||||
{% if 'ini' in value %}
|
||||
<div> ini entries:
|
||||
{% for ini in value.ini %}
|
||||
<p>[@{ ini.section }@ ]<br>@{ ini.key }@ = @{ value.default | default('VALUE') }@</p>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if 'vars' in value %}
|
||||
{% for myvar in value.vars %}
|
||||
<div>var: @{ myvar.name }@</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if 'env' in value %}
|
||||
{% for env in value.env %}
|
||||
<div>env:@{ env.name }@</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if 'vars' in value %}
|
||||
{% for myvar in value.vars %}
|
||||
<div>var: @{ myvar.name }@</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
{# description #}
|
||||
<td>
|
||||
<div class="cell-border">
|
||||
{% if value.description is string %}
|
||||
<div>@{ value.description | replace('\n', '\n ') | html_ify }@</div>
|
||||
{% else %}
|
||||
{% for desc in value.description %}
|
||||
<div>@{ desc | replace('\n', '\n ') | html_ify }@</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if 'aliases' in value and value.aliases %}
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: @{ value.aliases|join(', ') }@</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if value.description is string %}
|
||||
<div>@{ value.description | replace('\n', '\n ') | html_ify }@</div>
|
||||
{% else %}
|
||||
{% for desc in value.description %}
|
||||
<div>@{ desc | replace('\n', '\n ') | html_ify }@</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if 'aliases' in value and value.aliases %}
|
||||
<div style="font-size: small; color: darkgreen"><br/>aliases: @{ value.aliases|join(', ') }@</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% if value.suboptions %}
|
||||
|
@ -242,43 +245,49 @@ Facts returned by this module are added/updated in the ``hostvars`` host facts a
|
|||
.. raw:: html
|
||||
|
||||
<table border=0 cellpadding=0 class="documentation-table">
|
||||
{# Pre-compute the nesting depth to allocate columns #}
|
||||
{% set ns = namespace(maxdepth=1) %}
|
||||
{% for key, value in returnfacts|dictsort recursive %}
|
||||
{% set ns.maxdepth = [loop.depth, ns.maxdepth] | max %}
|
||||
{% if value.contains %}
|
||||
{% if value.contains.items %}
|
||||
@{ loop(value.contains.items()) }@
|
||||
{% elif value.contains[0].items %}
|
||||
@{ loop(value.contains[0].items()) }@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<th class="head"><div class="cell-border">Fact</div></th>
|
||||
<th class="head"><div class="cell-border">Returned</div></th>
|
||||
<th class="head" width="100%"><div class="cell-border">Description</div></th>
|
||||
<th colspan="@{ ns.maxdepth }@">Fact</th>
|
||||
<th>Returned</th>
|
||||
<th width="100%">Description</th>
|
||||
</tr>
|
||||
{% for key, value in returnfacts|dictsort recursive %}
|
||||
<tr class="return-value-column">
|
||||
<td>
|
||||
<div class="outer-elbow-container">
|
||||
{% for i in range(1, loop.depth) %}
|
||||
<div class="elbow-placeholder"> </div>
|
||||
{% endfor %}
|
||||
<div class="elbow-key">
|
||||
<b>@{ key }@</b>
|
||||
<br/><div style="font-size: small; color: red">@{ value.type }@</div>
|
||||
</div>
|
||||
</div>
|
||||
<tr>
|
||||
{% for i in range(1, loop.depth) %}
|
||||
<td class="elbow-placeholder"></td>
|
||||
{% endfor %}
|
||||
<td colspan="@{ ns.maxdepth - loop.depth0 }@" colspan="@{ ns.maxdepth - loop.depth0 }@">
|
||||
<b>@{ key }@</b>
|
||||
<br/><div style="font-size: small; color: red">@{ value.type }@</div>
|
||||
</td>
|
||||
<td><div class="cell-border">@{ value.returned | html_ify }@</div></td>
|
||||
<td>@{ value.returned | html_ify }@</td>
|
||||
<td>
|
||||
<div class="cell-border">
|
||||
{% if value.description is string %}
|
||||
<div>@{ value.description | html_ify }@
|
||||
{% if value.description is string %}
|
||||
<div>@{ value.description | html_ify }@
|
||||
</div>
|
||||
{% else %}
|
||||
{% for desc in value.description %}
|
||||
<div>@{ desc | html_ify }@
|
||||
</div>
|
||||
{% else %}
|
||||
{% for desc in value.description %}
|
||||
<div>@{ desc | html_ify }@
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<br/>
|
||||
{% if value.sample is defined and value.sample %}
|
||||
<div style="font-size: smaller"><b>Sample:</b></div>
|
||||
{# TODO: The sample should be escaped, using | escape or | htmlify, but both mess things up beyond repair with dicts #}
|
||||
<div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">@{ value.sample | replace('\n', '\n ') | html_ify }@</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<br/>
|
||||
{% if value.sample is defined and value.sample %}
|
||||
<div style="font-size: smaller"><b>Sample:</b></div>
|
||||
{# TODO: The sample should be escaped, using | escape or | htmlify, but both mess things up beyond repair with dicts #}
|
||||
<div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">@{ value.sample | replace('\n', '\n ') | html_ify }@</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{# ---------------------------------------------------------
|
||||
|
@ -308,41 +317,46 @@ Common return values are documented :ref:`here <common_return_values>`, the foll
|
|||
.. raw:: html
|
||||
|
||||
<table border=0 cellpadding=0 class="documentation-table">
|
||||
{% set ns = namespace(maxdepth=1) %}
|
||||
{% for key, value in returndocs|dictsort recursive %}
|
||||
{% set ns.maxdepth = [loop.depth, ns.maxdepth] | max %}
|
||||
{% if value.contains %}
|
||||
{% if value.contains.items %}
|
||||
@{ loop(value.contains.items()) }@
|
||||
{% elif value.contains[0].items %}
|
||||
@{ loop(value.contains[0].items()) }@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<th class="head"><div class="cell-border">Key</div></th>
|
||||
<th class="head"><div class="cell-border">Returned</div></th>
|
||||
<th class="head" width="100%"><div class="cell-border">Description</div></th>
|
||||
<th colspan="@{ ns.maxdepth }@">Key</th>
|
||||
<th>Returned</th>
|
||||
<th width="100%">Description</th>
|
||||
</tr>
|
||||
{% for key, value in returndocs|dictsort recursive %}
|
||||
<tr class="return-value-column">
|
||||
<td>
|
||||
<div class="outer-elbow-container">
|
||||
{% for i in range(1, loop.depth) %}
|
||||
<div class="elbow-placeholder"> </div>
|
||||
{% endfor %}
|
||||
<div class="elbow-key">
|
||||
<b>@{ key }@</b>
|
||||
<br/><div style="font-size: small; color: red">@{ value.type }@</div>
|
||||
</div>
|
||||
</div>
|
||||
<tr>
|
||||
{% for i in range(1, loop.depth) %}
|
||||
<td class="elbow-placeholder"> </td>
|
||||
{% endfor %}
|
||||
<td colspan="@{ ns.maxdepth - loop.depth0 }@">
|
||||
<b>@{ key }@</b>
|
||||
<br/><div style="font-size: small; color: red">@{ value.type }@</div>
|
||||
</td>
|
||||
<td><div class="cell-border">@{ value.returned | html_ify }@</div></td>
|
||||
<td>@{ value.returned | html_ify }@</td>
|
||||
<td>
|
||||
<div class="cell-border">
|
||||
{% if value.description is string %}
|
||||
<div>@{ value.description | html_ify |indent(4)}@</div>
|
||||
{% else %}
|
||||
{% for desc in value.description %}
|
||||
<div>@{ desc | html_ify |indent(4)}@</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<br/>
|
||||
{% if value.sample is defined and value.sample %}
|
||||
<div style="font-size: smaller"><b>Sample:</b></div>
|
||||
{# TODO: The sample should be escaped, using |escape or |htmlify, but both mess things up beyond repair with dicts #}
|
||||
<div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">@{ value.sample | replace('\n', '\n ') | html_ify }@</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if value.description is string %}
|
||||
<div>@{ value.description | html_ify |indent(4)}@</div>
|
||||
{% else %}
|
||||
{% for desc in value.description %}
|
||||
<div>@{ desc | html_ify |indent(4)}@</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<br/>
|
||||
{% if value.sample is defined and value.sample %}
|
||||
<div style="font-size: smaller"><b>Sample:</b></div>
|
||||
{# TODO: The sample should be escaped, using |escape or |htmlify, but both mess things up beyond repair with dicts #}
|
||||
<div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">@{ value.sample | replace('\n', '\n ') | html_ify }@</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{# ---------------------------------------------------------
|
||||
|
|
Loading…
Reference in a new issue