Skip to content

fix(ui): build the Task Type icon picker from the stylesheet FOG ships - #1344

Merged
mastacontrola merged 1 commit into
working-1.6from
fix-icon-picker-fa4-list
Aug 24, 2026
Merged

fix(ui): build the Task Type icon picker from the stylesheet FOG ships#1344
mastacontrola merged 1 commit into
working-1.6from
fix-icon-picker-fa4-list

Conversation

@mastacontrola

Copy link
Copy Markdown
Member

The bug

TaskType::iconlist() built the Task Type icon dropdown by parsing packages/web/management/other/_variables.scss — a Font Awesome 4.7.0 variables file that no stylesheet imports, that nothing regenerates, and whose only reader was this method. The Font Awesome 7 migration (#1336) did not touch it.

So the picker offered 786 v4 names, of which 148 no longer exist in the shipped build — including all seven names schema steps 361–367 had just repaired in the database:

plus-square-o · hdd-o · arrow-circle-o-down · arrow-circle-o-up · hourglass-o · flag-o · bookmark-o

Choosing one writes the broken state straight back into taskTypes.ttIcon, which is rendered as fas fa-<stored name> and draws nothing. Fixing the data while leaving the picker able to rewrite it is not a fix.

The fix

The list now comes from management/css/font-awesome.min.css — the same file the browser loads — so it cannot describe a different set of icons from the one that will render. 1,946 names instead of 786.

Brands are excluded. They resolve in the stylesheet but live only in the Brands font, and FOG renders stored names with fas, so an offered brand draws a tofu box (which section 1 of the FA7 test already calls out as worse to eyeball than an absent icon). The stylesheet does not label an icon with its font, but it is a concatenation and every brand declaration follows the brands font-family rule, so that rule's offset is the boundary. It is matched on Font Awesome <n> Brands rather than a fixed 7, so a version bump cannot silently turn the filter off. Regular needs no filter: in Font Awesome Free every regular icon name also exists in solid.

Parsed once per request and memoised — the two calls the Task Type edit page makes cost one read.

Two more defects fixed on the way

Both in the method being rewritten:

  • The glyph beside each name never rendered. The old code built &#xf02b with no trailing semicolon and passed it through Initiator::e(); htmlspecialchars cannot recognise that as an entity even with double_encode off, so every row showed the literal text &#xf02b. It now emits the character itself.
  • The file was opened on a CWD-relative path ('../management/other/...'). It is read from BASEPATH, like every other file the class layer touches.

Pinned

New section 6 in tests/fontawesome7-icon-names.test.php. It executes the real method rather than reading its source — a source check passes on code that opens the right file and parses it wrongly, and parsing is where the next version bump will break it. _faIcons() depends on nothing but BASEPATH and that file, so the two methods lift into a bare host class with no FOG boot; extraction failing is a hard failure, not a quiet pass.

Mutation-verified, three mutants, all killed:

Mutant Result
drop the brands filter 609 brand icons offered
read only the first class of each rule (drop FA's grouped alias selectors) a grouped alias goes missing
go back to the entity form escaped entity text, no glyph

The alias mutant survived the checks as first written, because they only asked that everything offered is valid, never that everything valid is offered. The alias check that closes it derives its expectation from the stylesheet at run time rather than naming icons, so it survives a version bump.

Suite: 145 passed, 0 failed. management/other/_variables.scss is deleted — nothing imports it and nothing else reads it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XqpPXAk7bEm8huH9WkiGk6

TaskType::iconlist() built the icon dropdown by parsing
management/other/_variables.scss -- a Font Awesome 4.7.0 variables file that
no stylesheet imported, that nothing regenerated, and whose only reader was
this method. The Font Awesome 7 migration did not touch it.

So the picker offered 786 v4 names, of which 148 no longer exist in the
shipped build. Among them were all seven names schema steps 361-367 had just
repaired in the database -- plus-square-o, hdd-o, arrow-circle-o-down,
arrow-circle-o-up, hourglass-o, flag-o and bookmark-o. Choosing one wrote the
broken state straight back into taskTypes.ttIcon, which is rendered as
`fas fa-<stored name>` and draws nothing. Fixing the data while leaving the
picker able to rewrite it is not a fix.

The list now comes from management/css/font-awesome.min.css -- the same file
the browser loads -- so it cannot describe a different set of icons from the
one that will render. 1,946 names instead of 786.

Brands are excluded. They resolve in the stylesheet but live only in the
Brands font, and FOG renders stored names with `fas`, so an offered brand
draws a tofu box. The stylesheet does not label an icon with its font, but it
is a concatenation and every brand declaration follows the brands
font-family rule, so that rule's offset is the boundary; it is matched on
`Font Awesome <n> Brands` rather than a fixed 7 so a version bump cannot
silently turn the filter off. Regular needs no filter: in Font Awesome Free
every regular icon name also exists in solid.

Two defects fixed on the way, both in the method being rewritten:

  - the glyph beside each name never rendered. The old code built `&#xf02b`
    with no trailing semicolon and passed it through Initiator::e(), and
    htmlspecialchars cannot recognise that as an entity even with
    double_encode off -- so every row showed the literal text. It now emits
    the character itself.
  - the file was opened on a CWD-relative path. It is read from BASEPATH,
    like every other file the class layer touches.

Parsed once per request and memoised, so the two calls the Task Type edit
page makes cost one read.

Pinned by a new section 6 in tests/fontawesome7-icon-names.test.php, which
executes the real method rather than reading its source -- a source check
passes on code that opens the right file and parses it wrongly, and parsing
is where the next version bump will break it. Mutation-verified, three
mutants, all killed:

  - drop the brands filter        -> 609 brand icons offered
  - read only the first class of
    each rule (drop FA's grouped
    alias selectors)              -> a grouped alias goes missing
  - go back to the entity form    -> escaped entity text, no glyph

The first alias mutant SURVIVED the checks as first written, because they
only asked that everything offered is valid, never that everything valid is
offered. The alias check that closes it derives its expectation from the
stylesheet at run time rather than naming icons, so it survives a version
bump.

management/other/_variables.scss is deleted: nothing imports it and nothing
else reads it.

Co-Authored-By: Claude <noreply@anthropic.com>
@mastacontrola
mastacontrola merged commit d55531d into working-1.6 Aug 24, 2026
7 checks passed
@mastacontrola
mastacontrola deleted the fix-icon-picker-fa4-list branch August 24, 2026 11:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant