bug #22439 [DX] [TwigBundle] Enhance the new exception page design (sustmi)

This PR was squashed before being merged into the 3.3 branch (closes #22439).

Discussion
----------

[DX] [TwigBundle] Enhance the new exception page design

| Q             | A
| ------------- | ---
| Branch?       | 3.3
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT
| Doc PR        | -

- [x] Fix the problem with wrapping wide lines (https://github.com/symfony/symfony/pull/20951#issuecomment-292201983)

I got motivated by recent PR https://github.com/symfony/symfony/pull/20951 and redesigned the exception page even more.

Compare before: ![before](https://cloud.githubusercontent.com/assets/885946/25052220/0756b74e-2151-11e7-98b6-c99fd9eaabec.png)
with after: ![after](https://cloud.githubusercontent.com/assets/885946/25052225/0c76581a-2151-11e7-96ff-eb502ee8e97b.png)
(Ignore the the "sf" toolbar icon in the middle of the page. This is how Firefox does full-page screenshots.)

The most noticeable changes:
- removed double line spacing (it just does not feel natural)
- file context increased from 3 to 10 lines (it helps me to better orientate in the code)
- added horizontal scrollbar for the cases when the code is wider
- the highlighted line color is more saturated in order to be noticed easily

Commits
-------

43212b9a90 [DX] [TwigBundle] Enhance the new exception page design
This commit is contained in:
Fabien Potencier 2017-07-06 10:10:35 +03:00
commit 626e9875a2
3 changed files with 53 additions and 52 deletions

View File

@ -1,28 +1,29 @@
{% if trace.file|default(false) %}
<span class="icon icon-close">{{ include('@Twig/images/icon-minus-square.svg') }}</span>
<span class="icon icon-open">{{ include('@Twig/images/icon-plus-square.svg') }}</span>
{% endif %}
<div class="trace-line-header {{ trace.file|default(false) ? 'sf-toggle' }}" data-toggle-selector="#trace-html-{{ prefix }}-{{ i }}" data-toggle-initial="{{ _display_code_snippet ? 'display' }}">
{% if trace.file|default(false) %}
<span class="icon icon-close">{{ include('@Twig/images/icon-minus-square.svg') }}</span>
<span class="icon icon-open">{{ include('@Twig/images/icon-plus-square.svg') }}</span>
{% endif %}
{% if trace.function %}
<span class="trace-class">{{ trace.class|abbr_class }}</span>{% if trace.type is not empty %}<span class="trace-type">{{ trace.type }}</span>{% endif %}<span class="trace-method">{{ trace.function }}</span><span class="trace-arguments">({{ trace.args|format_args }})</span>
{% endif %}
{% if trace.function %}
<span class="trace-class">{{ trace.class|abbr_class }}</span>{% if trace.type is not empty %}<span class="trace-type">{{ trace.type }}</span>{% endif %}<span class="trace-method">{{ trace.function }}</span><span class="trace-arguments">({{ trace.args|format_args }})</span>
{% endif %}
{% if trace.file|default(false) %}
{% set line_number = trace.line|default(1) %}
{% set file_link = trace.file|file_link(line_number) %}
{% set file_path = trace.file|format_file(line_number)|striptags|replace({ (' at line ' ~ line_number): '' }) %}
{% set file_path_parts = file_path|split(constant('DIRECTORY_SEPARATOR')) %}
<span class="block trace-file-path">
in
<a href="{{ file_link }}">{{ file_path_parts[:-1]|join(constant('DIRECTORY_SEPARATOR')) }}{{ constant('DIRECTORY_SEPARATOR') }}<strong>{{ file_path_parts|last }}</strong></a>
(line {{ line_number }})
</span>
{% endif %}
{% if trace.file|default(false) %}
{% set line_number = trace.line|default(1) %}
{% set file_link = trace.file|file_link(line_number) %}
{% set file_path = trace.file|format_file(line_number)|striptags|replace({ (' at line ' ~ line_number): '' }) %}
{% set file_path_parts = file_path|split(constant('DIRECTORY_SEPARATOR')) %}
<span class="block trace-file-path">
in
<a href="{{ file_link }}">{{ file_path_parts[:-1]|join(constant('DIRECTORY_SEPARATOR')) }}{{ constant('DIRECTORY_SEPARATOR') }}<strong>{{ file_path_parts|last }}</strong></a>
(line {{ line_number }})
</span>
{% endif %}
</div>
{% if trace.file|default(false) %}
<div id="trace-html-{{ prefix ~ '-' ~ i }}" class="trace-code sf-toggle-content">
{{ trace.file|file_excerpt(trace.line)|replace({
{{ trace.file|file_excerpt(trace.line, 5)|replace({
'#DD0000': '#183691',
'#007700': '#a71d5d',
'#0000BB': '#222222',

View File

@ -1,37 +1,33 @@
<div class="trace trace-as-html">
<table class="trace-details">
<thead class="trace-head">
<tr>
<th class="sf-toggle" data-toggle-selector="#trace-html-{{ index }}" data-toggle-initial="{{ expand ? 'display' }}">
<h3 class="trace-class">
<span class="trace-namespace">
{{ exception.class|split('\\')|slice(0, -1)|join('\\') }}
{{- exception.class|split('\\')|length > 1 ? '\\' }}
</span>
{{ exception.class|split('\\')|last }}
<div class="trace-details">
<div class="trace-head">
<span class="sf-toggle" data-toggle-selector="#trace-html-{{ index }}" data-toggle-initial="{{ expand ? 'display' }}">
<h3 class="trace-class">
<span class="trace-namespace">
{{ exception.class|split('\\')|slice(0, -1)|join('\\') }}
{{- exception.class|split('\\')|length > 1 ? '\\' }}
</span>
{{ exception.class|split('\\')|last }}
<span class="icon icon-close">{{ include('@Twig/images/icon-minus-square-o.svg') }}</span>
<span class="icon icon-open">{{ include('@Twig/images/icon-plus-square-o.svg') }}</span>
</h3>
<span class="icon icon-close">{{ include('@Twig/images/icon-minus-square-o.svg') }}</span>
<span class="icon icon-open">{{ include('@Twig/images/icon-plus-square-o.svg') }}</span>
</h3>
{% if exception.message is not empty and index > 1 %}
<p class="break-long-words trace-message">{{ exception.message }}</p>
{% endif %}
</th>
</tr>
</thead>
{% if exception.message is not empty and index > 1 %}
<p class="break-long-words trace-message">{{ exception.message }}</p>
{% endif %}
</span>
</div>
<tbody id="trace-html-{{ index }}" class="sf-toggle-content">
<div id="trace-html-{{ index }}" class="sf-toggle-content">
{% set _is_first_user_code = true %}
{% for i, trace in exception.trace %}
{% set _display_code_snippet = _is_first_user_code and ('/vendor/' not in trace.file) and ('/var/cache/' not in trace.file) and (trace.file is not empty) %}
{% if _display_code_snippet %}{% set _is_first_user_code = false %}{% endif %}
<tr>
<td class="trace-line {{ trace.file|default(false) ? 'sf-toggle' }}" data-toggle-selector="#trace-html-{{ index }}-{{ i }}" data-toggle-initial="{{ _display_code_snippet ? 'display' }}">
{{ include('@Twig/Exception/trace.html.twig', { prefix: index, i: i, trace: trace }, with_context = false) }}
</td>
</tr>
<div class="trace-line">
{{ include('@Twig/Exception/trace.html.twig', { prefix: index, i: i, trace: trace, _display_code_snippet: _display_code_snippet }, with_context = false) }}
</div>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>

View File

@ -89,30 +89,34 @@ header .container { display: flex; justify-content: space-between; }
.exception-illustration { flex-basis: 111px; flex-shrink: 0; height: 66px; margin-left: 15px; opacity: .7; }
.trace + .trace { margin-top: 30px; }
.trace-head { background-color: #e0e0e0; padding: 10px; }
.trace-head .trace-class { color: #222; font-size: 18px; font-weight: bold; line-height: 1.3; margin: 0; position: relative; }
.trace-head .trace-namespace { color: #999; display: block; font-size: 13px; }
.trace-head .icon { position: absolute; right: 0; top: 0; }
.trace-head .icon svg { height: 24px; width: 24px; }
.trace-details { background: #FFF; border: 1px solid #E0E0E0; box-shadow: 0px 0px 1px rgba(128, 128, 128, .2); margin: 1em 0; }
.trace-message { font-size: 14px; font-weight: normal; margin: .5em 0 0; }
.trace-details { table-layout: fixed; }
.trace-line { position: relative; padding-left: 36px; }
.trace-line.sf-toggle:hover { background: #F5F5F5; }
.trace-line { position: relative; padding-top: 8px; padding-bottom: 8px; }
.trace-line:hover { background: #F5F5F5; }
.trace-line a { color: #222; }
.trace-line .icon { opacity: .4; position: absolute; left: 10px; top: 11px; }
.trace-line .icon svg { height: 16px; width: 16px; }
.trace-line-header { padding-left: 36px; }
.trace-file-path, .trace-file-path a { margin-top: 3px; color: #999; color: #795da3; color: #B0413E; color: #222; font-size: 13px; }
.trace-file-path, .trace-file-path a { color: #999; color: #795da3; color: #B0413E; color: #222; font-size: 13px; }
.trace-class { color: #B0413E; }
.trace-type { padding: 0 2px; }
.trace-method { color: #B0413E; color: #222; font-weight: bold; color: #B0413E; }
.trace-arguments { color: #222; color: #999; font-weight: normal; color: #795da3; color: #777; padding-left: 2px; }
.trace-code { background: #FFF; font-size: 12px; margin: 10px 0 2px; padding: 10px; overflow-x: auto; }
.trace-code { background: #FFF; font-size: 12px; margin: 10px 10px 2px 10px; padding: 10px; overflow-x: auto; white-space: nowrap; }
.trace-code ol { margin: 0; float: left; }
.trace-code li { color: #969896; margin: 0; padding-left: 10px; float: left; width: 100%; }
.trace-code li + li { margin-top: 5px; }
.trace-code li.selected { background: #F8EEC7; padding: 3px 0 3px 10px; margin-top: 2px; }
.trace-code li.selected { background: #F7E5A1; margin-top: 2px; }
.trace-code li code { color: #222; white-space: nowrap; }
.trace-as-text .stacktrace { line-height: 1.8; margin: 0 0 15px; white-space: pre-wrap; }