Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SKOS XL popup on concept page #1718

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 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,82 @@ 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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a compensation for the border in the margin like so:

Suggested change
border-radius: 0;
border-radius: 0;
margin: -4px;

So that the text doesn't jitter when the button is pushed (Chromium, not sure how it would be with other browsers).

}

.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"
osma marked this conversation as resolved.
Show resolved Hide resolved
{% 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
Loading