Skip to content

Add hazard categories to SampleType, Sample, ReferenceDefinition and ReferenceSample (GHS + ISO 7010)#2890

Open
ramonski wants to merge 71 commits into
2.xfrom
feat/hazard-categories
Open

Add hazard categories to SampleType, Sample, ReferenceDefinition and ReferenceSample (GHS + ISO 7010)#2890
ramonski wants to merge 71 commits into
2.xfrom
feat/hazard-categories

Conversation

@ramonski
Copy link
Copy Markdown
Contributor

@ramonski ramonski commented May 4, 2026

Description of the issue/feature this PR addresses

The only hazard signal on a SampleType today is a boolean Hazardous flag, rendered everywhere as a generic warning triangle. There is no way to record which hazard a sample carries (acid, poison, flammable, biohazard, radioactive, etc.), so reports and listings cannot show the appropriate pictogram and laboratories handling a mix of hazardous materials cannot communicate the specific risk in their internal documents.

This PR introduces an optional hazard_categories field on SampleType (DX), ReferenceDefinition (AT) and ReferenceSample (AT) capturing the hazard pictogram categories. The boolean Hazardous flag stays untouched and authoritative — the new field is purely additive. The 9 official UN/ECE GHS pictograms plus 10 ISO 7010 hazard pictograms (biohazard, radioactive, non-ionising radiation, magnetic field, electricity, hot surface, hot content, hot steam, low temperature, asphyxiating atmosphere) are shipped under senaite/core/browser/static/images/. All SVGs are public domain, sourced from Wikimedia Commons. Custom widgets render the field with the matching pictogram next to each option in edit mode and as inline icons with tooltips in view mode.

SCR-20260505-iuaj

The existing form-adapter pattern is extended on all three content types so the categories field is hidden until Hazardous is checked, and the toggle survives a re-render after validation errors. The sample title viewlet, sample listing, reference sample title, reference sample listing and reference definition listing all render the pictograms (with tooltips) instead of the legacy single warning glyph, falling back to the W001 SVG when the boolean is set without specific categories.

A new senaite.core.api namespace exposes the helpers callers need (get_portal, get_portal_url, translate, get_pictogram_url, get_warning_pictogram_url, get_pictogram, get_pictograms_for_codes, get_pictograms_for_sample, get_pictograms_for_reference, get_attr). It is the gradual replacement for bika.lims.api.

Hazard codes

Codes are grouped by hazard family. The first 9 are the UN/ECE GHS pictograms, the rest are ISO 7010 warning signs covering hazards GHS does not address.

Code Pictogram Standard Family Use case
GHS01 ghs/GHS01.svg GHS Chemical Explosive
GHS02 ghs/GHS02.svg GHS Chemical Flammable
GHS03 ghs/GHS03.svg GHS Chemical Oxidizing
GHS04 ghs/GHS04.svg GHS Chemical Compressed gas
GHS05 ghs/GHS05.svg GHS Chemical Corrosive (acid / caustic)
GHS06 ghs/GHS06.svg GHS Chemical Acute toxicity
GHS07 ghs/GHS07.svg GHS Chemical Health hazard (harmful / irritant)
GHS08 ghs/GHS08.svg GHS Chemical Serious health hazard (CMR)
GHS09 ghs/GHS09.svg GHS Chemical Environmental hazard
BIO01 iso/W009.svg ISO 7010 Biological Biohazard (infectious / biological)
RAD01 iso/W003.svg ISO 7010 Radiation Radioactive (ionising radiation)
NIR01 iso/W005.svg ISO 7010 Radiation Non-ionising radiation (UV / laser / RF)
MAG01 iso/W006.svg ISO 7010 Radiation Magnetic field (NMR / MRI)
ELEC01 iso/W012.svg ISO 7010 Electrical Electricity (electric shock)
HSURF01 iso/W017.svg ISO 7010 Thermal Hot surface (hot to touch)
HOT01 iso/W079.svg ISO 7010 Thermal Hot content (heated material)
STEAM01 iso/W080.svg ISO 7010 Thermal Hot steam (autoclave / sterilisation)
COLD01 iso/W010.svg ISO 7010 Thermal Low temperature (freezing / cold store)
ASPH01 iso/W041.svg ISO 7010 Atmospheric Asphyxiating atmosphere (cryogenic / inert gas)

Codes are persistent identifiers (used as token and stored value) and are decoupled from the SVG filename so a pictogram can be re-sourced without a data migration. Custom prefixes are used for the ISO 7010 entries to avoid clashing with GHS or ISO W-codes.

Current behavior before PR

  • SampleType, ReferenceDefinition and ReferenceSample have only a boolean Hazardous field.
  • Sample.getHazardous() returns the SampleType boolean.
  • The sample listing, sample title and reference sample views all render a single generic warning glyph for any hazardous item.
  • The warning glyph uses the U+26A0 emoji codepoint, which Linux servers without an emoji font render as a placeholder box.
  • There is no way to differentiate acids from poisons from oxidizers, biohazards or radioactive samples in the UI or in printed documents.

Desired behavior after PR is merged

  • Schema: each of SampleType, ReferenceDefinition, ReferenceSample exposes hazard_categories (or HazardCategories in AT). Sample (AnalysisRequest) inherits its effective categories from the SampleType. ReferenceSample inherits from its ReferenceDefinition if no own value is set.
  • Edit form: each option renders as [checkbox] [pictogram] <code> — Name (synonym). The DX widget is a custom HazardCategoriesWidget (z3c.form CheckBoxWidget subclass). The AT widget is a custom HazardCategoriesWidget (subclass of MultiSelectionWidget) that ships its own view macro and reuses the standard checkbox edit macro plus styling from the bundle.
  • Toggle: the HazardCategories field stays hidden unless Hazardous is checked. Implemented via the senaite edit-form adapter on SampleType, ReferenceSample, ReferenceDefinition. Toggle reads the live form payload (suffix-tolerant for ZPublisher type markers like :list / :boolean), so it survives re-renders after validation errors.
  • Display contexts:
    • Sample title viewlet renders one pictogram per assigned category with the formatted label as title. Falls back to W001 when only the boolean is set.
    • Sample listing (samples/view.py) renders inline mini-pictograms from the SampleType brain in the setup catalog (resolved via the new getSampleTypeUID FieldIndex), no sample wakeup needed in the hot path.
    • Reference sample title and reference sample listing (bika/lims/browser/referencesample.py) render the pictograms via get_pictograms_for_reference (reads getHazardous and getHazardCategories on the brain / object, with fallback to the reference definition for ReferenceSample).
    • Reference definition listing (controlpanel/bika_referencedefinitions.py) renders the pictograms via the same helper.
    • Reference Definition / Reference Sample / Sample Type detail views render the pictograms via the AT or DX widget's view macro with title attributes on both the wrapping span and the inner img so tooltips show on hover.
  • Aspect ratio: pictogram <img> rules use object-fit: contain and the AT widget's ::before pseudo uses background-size: contain, so non-square ISO 7010 triangles are not squashed into the square pictogram box.
  • Catalog:
    • getHazardCategories and getHazardous are metadata columns on SETUP_CATALOG (read off the SampleType brain).
    • getSampleTypeUID is added as a FieldIndex on SAMPLE_CATALOG so listings can resolve the SampleType brain cheaply by UID.
    • The redundant getHazardous metadata column on SAMPLE_CATALOG is removed by the upgrade step (del_column): sample-level hazardous is resolved exclusively via the SampleType brain.
  • API: new senaite.core.api (re-uses senaite.core.i18n.translate; no bika.lims.api imports). All hazard-pictogram helpers, the portal helpers and a brain/object/UID-tolerant get_attr live here.
  • Bundle: hazard-categories CSS lives in webpack/app/scss/_hazard_categories.scss and ships in the existing senaite.core bundle (rebuilt as part of this PR). css-loader gains a url.filter option for the SCSS rule so absolute Plone resource URLs pass through verbatim.
  • Backward compatibility: existing getHazardous() semantics unchanged on objects. New AT fields default to []. Existing brains pre-upgrade fall through to the W001 fallback in listings until they are reindexed (the upgrade step reindexes existing SampleTypes).

Files of note

  • src/senaite/core/api/__init__.py — new flat senaite.core.api namespace with portal, translate, get_attr and pictogram helpers (Sphinx-style docstrings, no bika.lims.api dependency).
  • src/senaite/core/vocabularies/hazard_categories.pyHAZARD_CATEGORIES vocabulary grouped by hazard family; tokens are short codes so persistence is decoupled from translations.
  • src/senaite/core/z3cform/widgets/hazard_categories.py (DX) and src/senaite/core/browser/widgets/hazardcategorieswidget.py (AT) — custom widgets.
  • src/senaite/core/browser/static/images/ghs/GHS01..GHS09.svg, iso/W001.svg, iso/W003.svg, iso/W005.svg, iso/W006.svg, iso/W009.svg, iso/W010.svg, iso/W012.svg, iso/W017.svg, iso/W041.svg, iso/W079.svg, iso/W080.svg — public-domain SVG pictograms.
  • src/senaite/core/browser/form/adapters/{sampletype,referencesample,referencedefinition}.py — edit-form toggle.
  • src/senaite/core/browser/form/helpers.py — shared is_checked, get_form_value, has_form_field helpers used by the adapters.
  • src/senaite/core/upgrade/v02_07_000.py — upgrade step add_hazard_categories adds the getHazardCategories and getHazardous metadata columns on the setup catalog, the getSampleTypeUID FieldIndex on the sample catalog, removes the now-redundant getHazardous metadata column from the sample catalog, and reindexes existing SampleTypes.
  • src/senaite/core/tests/doctests/API_core.rst — doctest for the new API helpers.
  • German translations included for all new strings.

Verification

  • bin/test-senaite -m test_textual_doctests -t API_core — 1 test, 0 failures.
  • After upgrade: edit a SampleType, toggle Hazardous → categories field appears with all 19 pictograms; save; sample listing rows and sample titles show the assigned pictograms; ReferenceDefinition / ReferenceSample detail views and listings render the pictograms with tooltips.

--
I confirm I have tested this PR thoroughly and coded it according to PEP8
and Plone's Python styleguide standards.

ramonski added 30 commits May 4, 2026 12:59
Introduces a new optional ``hazard_categories`` field on SampleType
(DX) and ``HazardCategories`` field on AnalysisRequest (AT) that
captures the GHS pictogram categories for a sample. Backwards
compatible with the existing ``hazardous`` boolean — that field is
the authoritative "treat as hazardous" gate and is left untouched.

- Vocabulary at ``senaite.core.vocabularies.hazard_categories``
  exposing the 9 official GHS codes (GHS01..GHS09) with translatable
  formal name and common synonym. Tokens are the GHS codes so
  translations and labels do not affect persistence.

- Sample-level field overrides the SampleType default. ``Sample.
  getHazardCategories()`` returns the override if set, else falls
  back to the SampleType. ``Sample.getHazardous()`` is unchanged.

- Official UN/ECE GHS pictogram SVGs (public domain, sourced from
  Wikimedia Commons) shipped under
  ``browser/static/images/ghs/GHSxx.svg``.

- ``hazard-categories.css`` styles the multi-checkbox widget so each
  category renders its pictogram next to the label in the edit form.
  Loaded as a separate stylesheet via the new
  ``HazardCategoriesStylesheet`` viewlet so the rules can evolve
  without a webpack rebuild.

- German translations included for all new strings.

- Upgrade step 2726 -> 2727 reapplies the viewlets profile so the
  stylesheet viewlet becomes active on existing instances.
Replace the standalone JS toggle with the proper SENAITE edit form
adapter pattern. The existing sampletype form adapter now hides
hazard_categories on initialize when Hazardous is unchecked, and
shows or hides it in real-time when the user toggles the checkbox.
The default OrderedSelectFieldWidget for List(Choice) draws two
<select> listboxes which cannot host inline pictograms via CSS.
Switching to CheckBoxFieldWidget produces a label-per-option
layout that the hazard-categories.css rules can decorate.
Replace the CSS-only :has() approach with a dedicated z3c.form
checkbox widget. Each option emits a real <img> pointing to the
matching GHS pictogram, with the GHS code as alt and the
formatted label as title (tooltip). Drops the brittle :has()
selectors and gives screen readers proper alt text.
Without extending ISequenceWidget, @implementer_only stripped
the parent's ISequenceWidget marker. The converter lookup then
fell through to FieldWidgetDataConverter which requires the
field to provide IFromUnicode — schema.List does not.
Used as the default hazard pictogram when a sample is marked
hazardous but no specific GHS category has been selected.
Mirrors the existing GHS SVG pattern so renderers (PDF, listing)
no longer depend on the U+26A0 emoji font being installed.
The SampleType view page renders the field in display mode, which
triggered a ComponentLookupError because only the input template
was registered. The display template renders selected categories
as a row of pictograms with tooltips.
The display template wraps each pictogram <img> in a 32x32 span,
but the inner <img> was unconstrained and rendered at the SVG's
intrinsic (huge) size. Add explicit width/height/object-fit on
the <img> so it fills only the wrapper.
- Add getHazardCategories as a metadata column on SAMPLE_CATALOG
  so listings can render the new pictograms without waking up
  each sample.
- New SampleType modified subscriber walks dependent samples and
  reindexes the hazard metadata when hazardous or hazard_categories
  changes. Standard catalog pattern; SampleType edits are rare
  while listings render constantly, so the cost lives at the right
  end.
- Sample title viewlet renders one <img> per assigned GHS category
  with the category code as alt and the formatted name as tooltip.
  Falls back to the ISO 7010 W001 'General warning' pictogram when
  the sample is hazardous but no category was selected.
- Upgrade step v02_07_000.add_hazard_categories now also adds the
  new metadata column and reindexes all existing samples once.
Both the sample title viewlet and the edit-form widget now call
into senaite.core.api.hazard_categories. The API exposes:

- get_pictogram_url(code) for a single GHS pictogram
- get_warning_pictogram_url() for the ISO 7010 W001 fallback
- get_pictogram(code) returning a {url, alt, title} view-model
- get_pictograms_for_sample(sample) wrapping the boolean fallback

Centralizing the URL building keeps the viewlet, the widget, and
any future consumer (custom reports, listing columns) in sync.
The earlier rule img.hazard-pictogram{width:100%} was more
specific than .hazard-pictogram{width:28px}, so it overrode
the edit-form size and the icon expanded to fill its container.
Restrict the 100% rule to span.hazard-pictogram > img which
only matches display mode.
- Move the pictogram helpers into senaite.core.api so callers use
  the flat namespace (api.get_pictogram_url,
  api.get_pictograms_for_sample, ...). Matches the long-term
  direction of folding bika.lims.api into senaite.core.api.
- The AnalysisRequest LinesField was wired with
  vocabulary="senaite.core.vocabularies.hazard_categories", which
  AT could not resolve and therefore iterated the string
  character-by-character. Replace with a getHazardCategoriesVocabulary
  method that returns a DisplayList; also surfaces the registry-based
  label overrides at runtime.
The hazard reindex subscriber needs to find samples by SampleType
UID. The column existed only as metadata; promoting it to a
FieldIndex is a small change with no downside (~1 entry per sample,
single-value index). Upgrade step adds the index and reindexes.
Replace the single hazardous.png glyph in samples/view.py with one
pictogram per assigned GHS category, using the brain metadata via
api.get_pictograms_for_codes — no sample wakeup required. Adds a
.hazard-pictogram-mini CSS class sized for inline listing icons.
Existing brains do not carry the getHazardCategories metadata
column until the upgrade step runs, so attribute access climbs
the acquisition chain up to the RequestContainer. Use getattr
with None default; api.get_pictograms_for_codes treats it as
'no specific category, fall back to W001'.
after_icons accumulates as a Py2 bytes string elsewhere in this
folderitem method. The unicode template combined with translated
German titles from the GHS vocabulary triggered the implicit
ASCII decode. Encode the formatted markup to utf-8 to match the
bytes-str expectation of the surrounding code.
Per first-iteration scope: keep hazard categories on SampleType
only. Sample.getHazardCategories() now reads exclusively from the
SampleType, no per-sample override field. Removes the AT
LinesField, the DisplayList vocabulary helper, the unused
LinesField/MultiSelectionWidget imports, and the AR-specific
i18n strings.

Sample-level override can be revisited in a later iteration if
labs need the granularity.
Closed samples (published, invalid, cancelled, rejected) are
schema-frozen, so refreshing their hazard metadata serves no
purpose. Limiting the catalog query to active states cuts the
reindex work proportionally on installs with many historical
samples — the most common large-DB shape.
senaite.core.api should not import bika.lims.api as the migration
direction is the opposite. Replace the bika.lims.api.get_portal
call with native get_portal/get_portal_url helpers built on
zope.component.hooks.getSite. Pictogram helpers no longer take a
portal kwarg — internal lookup is cheap enough.
Adopts the reStructuredText :param/:type/:returns/:rtype style
already used in bika.lims.api so the migration of helpers from
bika.lims.api to senaite.core.api lands with consistent docs.

Adds src/senaite/core/tests/test_api.py covering get_portal,
get_portal_url, get_pictogram_url, get_warning_pictogram_url,
get_pictogram, and get_pictograms_for_codes (known/unknown
codes, override mapping, hazardous=False, empty fallback,
None codes).
Doctests are the canonical style for senaite.core API tests
(see API_geo.rst, API_datetime.rst, API_user.rst). The doctest
covers the same surface as the deleted unit test: portal
helpers, GHS pictogram URL lookup, view-model dicts, label
overrides, and the W001 fallback path.

Run: bin/test-senaite -m test_textual_doctests -t API_core
WARNING_LABEL was a plain unicode string and ended up untranslated
inside <img alt> / <title> attributes. Make it a senaite
MessageFactory message and resolve it via a new
senaite.core.api.translate helper that uses the current request
locale. Adds the German translation 'Gefährlich'.
- Drop the local zope.i18n wrapper from senaite.core.api in favor of
  the existing senaite.core.i18n.translate helper. Re-exported as
  api.translate so callers can keep using api.translate(...).
- The upgrade step called bika.lims.api.get_setup_tool which does
  not exist; use api.get_tool('portal_setup') instead.
get_overridden_labels read a registry record that nothing ever
wrote, and there was no UI to set it. Translation files cover
the per-installation customization use case; if a runtime
relabeling story emerges later, it can be wired in cleanly
on top of this.
zope.i18n.translate can return bytes-str on Py2 when a
translation message is loaded for the active locale. When
format_title then concatenated those bytes into a unicode
template, samples/view.py raised UnicodeDecodeError on the
first non-ASCII character (e.g. 'Ätzend'). Wrap each
translated value in api.safe_unicode so format_title always
returns unicode.
senaite.core.i18n.translate defaults to to_utf8=True, which
returned bytes for the German 'Gefährlich'. Downstream unicode
string-formatting in the sample listing then failed with
UnicodeDecodeError. Pass to_utf8=False so the helper returns
unicode and the bytes/unicode boundary stays at the rendering
layer.
Mirror the SampleType/Sample feature on the QC side:

- ReferenceDefinition gains a HazardCategories LinesField + the
  getHazardCategoriesVocabulary helper, alongside the existing
  Hazardous boolean.
- ReferenceSample gains the same field and helper. Its
  getHazardCategories() returns the sample-level value when set,
  otherwise inherits from the linked ReferenceDefinition. The
  legacy own Hazardous boolean is unchanged.
- Translations added for the four new label/description strings.
@ramonski ramonski added the Enhancement ✨ Improvement to existing functionality label May 5, 2026
@ramonski ramonski requested a review from xispa May 5, 2026 07:57
ramonski added 12 commits May 5, 2026 10:02
…data

Move the ``getHazardCategories`` metadata column off the sample
catalog and onto the setup catalog (where SampleTypes already live),
then resolve a sample's hazard categories at render time by:

1. consulting an optional per-sample override
   ``getCustomHazardCategories`` (placeholder method on
   AnalysisRequest, currently always empty);
2. falling back to a UID lookup of the SampleType brain in the
   setup catalog and reading ``getHazardCategories`` metadata from
   there.

This drops the bulk-reindex subscriber that walked every active
sample on a SampleType edit, and the matching loop in the upgrade
step. SampleType edits now only need a normal reindex of the
SampleType itself.

Listings call ``api.get_pictograms_for_sample(brain)`` and the
helper does the lookup centrally so callers don't have to thread
codes through.
The helper does not depend on samples in any way: it gets a
named attribute or method-call result from any content object or
catalog brain. Lifted it out of the private namespace, renamed
the parameter, added a ``default`` argument and covered it with
doctests.
Inline the hazard-category resolution into
``get_pictograms_for_sample`` and lift the only piece of reusable
logic out as ``api.get_brain_attr(uid, name, catalog, default=None)``,
which looks an object up by UID in a catalog and reads a metadata
attribute from its brain without waking the object up.

Drop the underscore-prefixed sample-specific helpers from the API
namespace and add a doctest section covering the new helper.
Drop the separate ``get_brain_attr`` helper. ``api.get_attr`` now
takes an optional ``catalog`` keyword: when given, ``obj`` is
treated as a UID, looked up in that catalog, and the attribute is
read from the matching brain. Without ``catalog``, the function
behaves exactly as before. Update the call site in
``get_pictograms_for_sample`` and the doctests accordingly.
Accept any of the three forms ``bika.lims.api.get_uid`` handles
(content object, catalog brain or UID string). Validate the input
once via ``is_object``/``is_uid`` so callers don't have to wrap
the call in try/except, and use ``bika.lims.api.search`` for the
UID lookup when a catalog is given.

The hazard-resolution call site stays the same: passing the
SampleType UID through ``get_attr(uid, "getHazardCategories",
catalog=SETUP_CATALOG)`` reads the metadata from the SampleType
brain without waking it up.
The doctests construct plain Python objects to exercise the
attribute/method dispatch, which the bika-content validity check
rejected, causing the function to return default for a
non-Plone input. Move the check inside the catalog is not None
branch where it is actually needed; in direct-attribute mode any
input that has the requested attribute now works again.
@ramonski ramonski changed the title Add GHS hazard categories to SampleType and Sample Add hazard categories to SampleType and Sample (GHS + ISO 7010) May 10, 2026
ramonski added 6 commits May 10, 2026 22:39
Extend the hazard categories vocabulary with 6 ISO 7010 pictograms
covering hazards GHS does not address:

  BIO01  W009  Biohazard
  RAD01  W003  Radioactive (ionising radiation)
  NIR01  W005  Non-ionising radiation
  ELEC01 W012  Electricity (electric shock)
  COLD01 W010  Low temperature
  HOT01  W079  Hot content

Custom code prefixes (BIO, RAD, NIR, ELEC, COLD, HOT) are used as
persistent identifiers so the SVG source can be re-sourced without
a data migration.

Resolve hazardous flag and categories via the SampleType brain so
SampleType edits are reflected in listings without reindexing
samples. Add getHazardous as a setup-catalog metadata column and
extend the upgrade step to populate it.

Add object-fit:contain to the pictogram <img> rules and
background-size:contain to the AT widget pseudo-element so the
non-square ISO 7010 triangles preserve their aspect ratio in the
square pictogram box. Broaden the AT widget selector from
input[value^="GHS"] to input[type="checkbox"] and add
background-image rules for the new codes.
The Wikimedia source SVGs for W005, W009, W010 and W079 each draw
their inner glyph at a different inset relative to the triangle
border (varying from ~40% to ~50% of triangle height), making the
pictograms render at visibly different sizes when displayed side
by side at the same box dimensions.

Wrap each verbose SVG's inner-symbol group with an additional
scale-1.3-around-(300,320) transform so the inner glyph fills
~57% of the triangle, matching the visual weight of the W003 and
W012 minimalist source SVGs. The triangle border, yellow fill and
inner-glyph geometry from the official ISO 7010 Wikimedia sources
are preserved.
W079 from Wikimedia ships with #ffe100 yellow, while the other ISO
7010 SVGs we use (W003, W005, W009, W010, W012) use #f9a800 — the
specified RAL 1003 'signal yellow' color. Match the rest.
…e, ReferenceDefinition and ReferenceSample

- Rename GHS_CATEGORIES -> HAZARD_CATEGORIES and GHS_PICTOGRAM_PATH ->
  PICTOGRAM_PATH; scrub 'GHS hazard categories' wording in docstrings,
  field descriptions, doctests, ZCML, CHANGES and DE translations.
- Add HazardCategories field on ReferenceDefinition and ReferenceSample
  (AT LinesField) with inheritance: sample-level overrides win, else
  fall back to the reference definition.
- Render hazard pictograms in the reference sample title, reference
  sample listing and reference definition listing (replaces the legacy
  hazardous_big.png / hazardous.png).
- Add get_pictograms_for_reference helper for any object exposing
  getHazardous / getHazardCategories.
- Resolve sample-level hazardous flag exclusively via the SampleType
  brain in setup_catalog; remove the redundant getHazardous metadata
  column from sample_catalog and add a del_column step.
- Add 4 more ISO 7010 categories (MAG01 magnetic field, HSURF01 hot
  surface, ASPH01 asphyxiating atmosphere, STEAM01 hot steam) and
  reorder the vocabulary by hazard family (biological, radiation /
  fields, electrical, thermal, atmospheric).
- Set title attributes on both the wrapping span and inner img in the
  AT and DX view templates so tooltips show on hover.
- Make .hazard-pictograms title styling generic (no longer scoped to
  .sample-title) so reference sample titles get the same sizing.
@ramonski ramonski changed the title Add hazard categories to SampleType and Sample (GHS + ISO 7010) Add hazard categories to SampleType, Sample, ReferenceDefinition and ReferenceSample (GHS + ISO 7010) May 11, 2026
ramonski added 6 commits May 11, 2026 13:55
The modal title was set via plain text, so the hazard pictograms
attached to the listing row never made it into the popover header.
Pick them up from the row's title cell, append after the title text
and rebuild the bundle.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Enhancement ✨ Improvement to existing functionality

Development

Successfully merging this pull request may close these issues.

1 participant