Skip to content

Commit

Permalink
Merge pull request #1718 from NatLibFi/issue1714-concept-page-skos-xl
Browse files Browse the repository at this point in the history
SKOS XL popup on concept page
  • Loading branch information
osma authored Dec 17, 2024
2 parents 30d6246 + b866579 commit 40ce410
Show file tree
Hide file tree
Showing 5 changed files with 295 additions and 139 deletions.
83 changes: 83 additions & 0 deletions resource/css/skosmos.css
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
--vocab-table-border: var(--medium-color);
--vocab-link: var(--secondary-dark-color);
--vocab-box-text: var(--dark-color);
--tooltip-fg-color: var(--light-color);
--tooltip-bg-color: var(--dark-color);
--tooltip-border-color: var(--secondary-medium-color);

/* Font definitions */
--font-size: 14px;
Expand Down Expand Up @@ -951,3 +954,83 @@ body {
font-size: 20px !important;
}

/* Tooltips */

.tooltip-html {
position: relative;
display: inline-block;
cursor: pointer;
}

#concept-label .tooltip-html {
bottom: 4px;
}

.tooltip-html button {
line-height: 1.0;
padding: 3px;
margin: -3px;
}

.tooltip-html button:focus {
border: 2px solid var(--vocab-text);
border-radius: 0;
margin: -4px;
}

.tooltip-html:hover .tooltip-html-content, .tooltip-html:focus-within .tooltip-html-content {
visibility: visible;
opacity: 1;
}

.tooltip-html-content {
box-sizing: border-box;
position: absolute;
top: 20px;
left: 0;
display: block;
cursor: text;
width: 400px;
color: var(--tooltip-fg-color);
background-color: var(--tooltip-bg-color);
border: 3px solid var(--tooltip-border-color);
z-index: 9001;
visibility: hidden;
opacity: 0;
padding: 0.25rem 0.5rem;
}

/* The triangle that appears near the element that triggered it. */
.tooltip-html-content:after {
position: absolute;
content: '';
width: 10px;
height: 10px;
transform: rotate(45deg);
top: -7px;
left: 0;
margin-left: 2px;
color: var(--tooltip-fg-color);
border-top: 6px solid var(--tooltip-border-color);
border-right: 6px solid transparent;
border-bottom: 6px solid transparent;
border-left: 6px solid var(--tooltip-border-color);
z-index: 9002;
}

/* Definition lists used in tooltips. */

dl.tooltip-html-content dt {
display: block;
float: left;
font-weight: bold;
margin-right: 0.4em;
}

dl.tooltip-html-content dt::after {
content: ":";
}

dl.tooltip-html-content dd {
display: block;
}
37 changes: 30 additions & 7 deletions src/view/concept-card.inc.twig
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,13 @@
{%- endif -%}
</div>
<div class="col-sm-8" id="concept-label">
{% if concept.hasXlLabel %}
{% set descriptionId = 'concept-preflabel-xl' %}
{{ include('xl-label.inc.twig', {xlLabel: concept.xlLabel, xlDescriptionId: descriptionId}) }}
{% endif %}
{% if concept.notation %}
<h1 id="concept-preflabel"
{% if descriptionId %}aria-describedby="{{ descriptionId }}"{% endif %}
class="mb-0">
<span class="notation user-select-all" id="concept-notation">{{ concept.notation }}</span> {{ concept.label }}</h1><button
class="btn btn-default copy-clipboard px-1" type="button" id="copy-notation"
Expand All @@ -46,6 +51,7 @@
</button>
{% else %}
<h1 id="concept-preflabel"
{% if descriptionId %}aria-describedby="{{ descriptionId }}"{% endif %}
class="mb-0 user-select-all">{{ concept.label }}</h1><button
class="btn btn-default copy-clipboard px-1" type="button" id="copy-preflabel"
data-bs-toggle="tooltip" data-bs-placement="button" title="{{ 'Copy to clipboard' | trans }}">
Expand Down Expand Up @@ -78,7 +84,17 @@
</a>
</li>
{% else %} {# literals, e.g. altLabels #}
<li>{% if propval.containsHtml %}{{ propval.label|raw }}{% else %}{{ propval.label }}{% endif %}</li>
<li>
{%- apply spaceless %}
{% if propval.hasXlProperties %}
{% set descriptionId = "concept-property-xl-#{loop.parent.loop.index}-#{loop.index}" %}
{{ include('xl-label.inc.twig', {xlLabel: propval.xlLabel, xlDescriptionId: descriptionId}) }}
{% else %}
{% set descriptionId = '' %}
{% endif %}
<span{% if descriptionId %} aria-describedby="{{ descriptionId }}"{% endif %}>{% if propval.containsHtml %}{{ propval.label|raw }}{% else %}{{ propval.label }}{% endif %}</span>
{% endapply -%}
</li>
{% endif %}
{% endfor %}
</ul>
Expand All @@ -96,14 +112,21 @@
<div class="col-sm-5">
<ul>
{% for value in labels.prefLabel|default([])|merge(labels.altLabel|default([])) %}
{% if value.type == "skos:prefLabel" and value.lang in request.vocab.config.languages %}
<li>
<a href="{{ concept.uri|link_url(request.vocabid,request.lang, 'page', value.lang) }}"
hreflang="{{ value.lang }}">{{ value.label }}</a>
</li>
{% if value.hasXlProperties %}
{% set descriptionId = "concept-property-xl-#{loop.parent.loop.index}-#{loop.index}" %}
{{ include('xl-label.inc.twig', {xlLabel: value.xlLabel, xlDescriptionId: descriptionId}) }}
{% else %}
<li class="altlabel">{{ value.label }}</li>
{% set descriptionId = '' %}
{% endif %}
{% if value.type == "skos:prefLabel" and value.lang in request.vocab.config.languages %}
<a {% if descriptionId %}aria-describedby="{{ descriptionId }}"{% endif %}
href="{{ concept.uri|link_url(request.vocabid,request.lang, 'page', value.lang) }}"
hreflang="{{ value.lang }}">{{ value.label }}</a>
{% else %}
<span class="altlabel"{% if descriptionId %} aria-describedby="{{ descriptionId }}"{% endif %}>{{ value.label }}</span>
{% endif %}
</li>
{% endfor %}
</ul>
</div>
Expand Down Expand Up @@ -133,7 +156,7 @@
<a class="me-3" href="rest/v1/{{ vocab.id }}/data?uri={{ concept.uri|url_encode }}&amp;format=application/rdf%2Bxml">RDF/XML</a>
</li>
<li>
<a class="me-3"href="rest/v1/{{ vocab.id }}/data?uri={{ concept.uri|url_encode }}&amp;format=text/turtle">TURTLE</a>
<a class="me-3" href="rest/v1/{{ vocab.id }}/data?uri={{ concept.uri|url_encode }}&amp;format=text/turtle">TURTLE</a>
</li>
<li>
<a href="rest/v1/{{ vocab.id }}/data?uri={{ concept.uri|url_encode }}&amp;format=application/ld%2Bjson">JSON-LD</a>
Expand Down
9 changes: 9 additions & 0 deletions src/view/xl-label.inc.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<div class="tooltip-html">
<button class="btn" aria-label="{{ 'More information about this term'|trans }}" aria-controls="{{ xlDescriptionId }}"><i class="fa-solid fa-circle-info"></i></button>
<dl class="tooltip-html-content" id="{{ xlDescriptionId }}">
{% for key, val in xlLabel.properties %}
<dt>{{ key|trans }}</dt>
<dd>{{ val }}</dd>
{% endfor %}
</dl>
</div>
131 changes: 131 additions & 0 deletions tests/cypress/template/concept-full-vs-partial.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
describe('Concept page, full vs. partial page loads', () => {
const pageLoadTypes = ["full", "partial"]

// tests that should be executed both with and without partial page load
pageLoadTypes.forEach((pageLoadType) => {
it('contains concept preflabel / ' + pageLoadType, () => {
if (pageLoadType == "full") {
cy.visit('/yso/en/page/p39473') // go to "burial mounds" concept page
} else {
cy.visit('/yso/en/page/p5714') // go to "prehistoric graves" concept page
// click on the link to "burial mounds" to trigger partial page load
cy.get('#tab-hierarchy').contains('a', 'burial mounds').click()
}

// check that the vocabulary title is correct
cy.get('#vocab-title > a').invoke('text').should('equal', 'YSO - General Finnish ontology (archaeology)')

// check the concept prefLabel
cy.get('#concept-heading h1').invoke('text').should('equal', 'burial mounds')
})
it('concept preflabel can be copied to clipboard / ' + pageLoadType, () => {
if (pageLoadType == "full") {
cy.visit('/yso/en/page/p39473') // go to "burial mounds" concept page
} else {
cy.visit('/yso/en/page/p5714') // go to "prehistoric graves" concept page
// click on the link to "burial mounds" to trigger partial page load
cy.get('#tab-hierarchy').contains('a', 'burial mounds').click()
}

// click the copy to clipboard button next to the prefLabel
cy.get('#copy-preflabel').click()

// check that the clipboard now contains "music pyramids"
// NOTE: This test may fail when running Cypress interactively in a browser.
// The reason is browser security policies for accessing the clipboard.
// If that happens, make sure the browser window has focus and re-run the test.
cy.window().its('navigator.clipboard').invoke('readText').then((result) => {}).should('equal', 'burial mounds');
})
it('contains concept URI / ' + pageLoadType, () => {
if (pageLoadType == "full") {
cy.visit('/yso/en/page/p39473') // go to "burial mounds" concept page
} else {
cy.visit('/yso/en/page/p5714') // go to "prehistoric graves" concept page
// click on the link to "burial mounds" to trigger partial page load
cy.get('#tab-hierarchy').contains('a', 'burial mounds').click()
}

// check the property name
cy.get('.prop-uri .property-label').invoke('text').should('equal', 'URI')

// check the concept URI
cy.get('#concept-uri').invoke('text').should('equal', 'http://www.yso.fi/onto/yso/p39473')
})
it('concept URI can be copied to clipboard / ' + pageLoadType, () => {
if (pageLoadType == "full") {
cy.visit('/yso/en/page/p39473') // go to "burial mounds" concept page
} else {
cy.visit('/yso/en/page/p5714') // go to "prehistoric graves" concept page
// click on the link to "burial mounds" to trigger partial page load
cy.get('#tab-hierarchy').contains('a', 'burial mounds').click()
}

// click the copy to clipboard button next to the URI
cy.get('#copy-uri').click()

// check that the clipboard now contains "http://www.yso.fi/onto/yso/p39473"
// NOTE: This test may fail when running Cypress interactively in a browser.
// The reason is browser security policies for accessing the clipboard.
// If that happens, make sure the browser window has focus and re-run the test.
cy.window().its('navigator.clipboard').invoke('readText').then((result) => {}).should('equal', 'http://www.yso.fi/onto/yso/p39473');
})
it('concept notation can be copied to clipboard / ' + pageLoadType, () => {
if (pageLoadType == "full") {
cy.visit('/test-notation-sort/en/page/?uri=http%3A%2F%2Fwww.skosmos.skos%2Ftest%2Fta0115') // go to "Eel" concept page
} else {
cy.visit('/test-notation-sort/en/page/?uri=http%3A%2F%2Fwww.skosmos.skos%2Ftest%2Fta0114') // go to "Buri" concept page
// click on the link to "Eel" to trigger partial page load
cy.get('#tab-hierarchy').contains('a', 'Eel').click()
}

// click the copy to clipboard button next to the URI
cy.get('#copy-notation').click()

// check that the clipboard now contains "33.2"
// NOTE: This test may fail when running Cypress interactively in a browser.
// The reason is browser security policies for accessing the clipboard.
// If that happens, make sure the browser window has focus and re-run the test.
cy.window().its('navigator.clipboard').invoke('readText').then((result) => {}).should('equal', '33.2');
})
it('contains mappings / ' + pageLoadType, () => {
if (pageLoadType == "full") {
cy.visit('/yso/en/page/p14174') // go to "labyrinths" concept page
} else {
cy.visit('/yso/en/page/p5714') // go to "prehistoric graves" concept page
// click on the link to "labyrinths" to trigger partial page load
cy.get('#tab-hierarchy').contains('a', 'labyrinths').click()
}

// check that we have some mappings
cy.get('#concept-mappings').should('not.be.empty')
// check that spinner does not exist after load
cy.get('#concept-mappings i.fa-spinner', {'timeout': 15000}).should('not.exist')

// check the first mapping property name
// NOTE: we need to increase the timeout as the mappings can take a long time to load
cy.get('.prop-mapping h2', {'timeout': 20000}).eq(0).contains('Closely matching concepts')
// check the first mapping property values
cy.get('.prop-mapping').eq(0).find('.prop-mapping-label').eq(0).contains('Labyrinths')
cy.get('.prop-mapping').eq(0).find('.prop-mapping-label').eq(0).find('a').should('have.attr', 'href', 'http://id.loc.gov/authorities/subjects/sh85073793')
cy.get('.prop-mapping').eq(0).find('.prop-mapping-vocab').eq(0).contains('Library of Congress Subject Headings')
// check that the first mapping property has the right number of entries
cy.get('.prop-mapping').eq(0).find('.prop-mapping-label').should('have.length', 1)

// check the second mapping property name
cy.get('.prop-mapping h2').eq(1).contains('Exactly matching concepts')
// check the second mapping property values
cy.get('.prop-mapping').eq(1).find('.prop-mapping-label').eq(0).contains('labyrinter (sv)')
cy.get('.prop-mapping').eq(1).find('.prop-mapping-label').eq(0).find('a').invoke('text').should('equal', 'labyrinter')
cy.get('.prop-mapping').eq(1).find('.prop-mapping-label').eq(0).find('a').should('have.attr', 'href', 'http://www.yso.fi/onto/allars/Y21700')
cy.get('.prop-mapping').eq(1).find('.prop-mapping-vocab').eq(0).contains('Allärs - General thesaurus in Swedish')
// skipping the middle one (mapping to KOKO concept) as it's similar to the others
cy.get('.prop-mapping').eq(1).find('.prop-mapping-label').eq(2).contains('labyrintit (fi)')
cy.get('.prop-mapping').eq(1).find('.prop-mapping-label').eq(2).find('a').invoke('text').should('equal', 'labyrintit')
cy.get('.prop-mapping').eq(1).find('.prop-mapping-label').eq(2).find('a').should('have.attr', 'href', 'http://www.yso.fi/onto/ysa/Y108389')
cy.get('.prop-mapping').eq(1).find('.prop-mapping-vocab').eq(2).contains('YSA - Yleinen suomalainen asiasanasto')
// check that the second mapping property has the right number of entries
cy.get('.prop-mapping').eq(1).find('.prop-mapping-label').should('have.length', 3)
})

});
})
Loading

0 comments on commit 40ce410

Please sign in to comment.