diff --git a/src/components/annotations/AnnotationsView.vue b/src/components/annotations/AnnotationsView.vue index cd12a345..d9656621 100644 --- a/src/components/annotations/AnnotationsView.vue +++ b/src/components/annotations/AnnotationsView.vue @@ -35,6 +35,7 @@ interface Props { types: AnnotationType[] } const props = defineProps(); +const emit = defineEmits(['init']) const annotations = computed(() => annotationStore.annotations); const filteredAnnotations = computed(() => annotationStore.filteredAnnotations); @@ -53,14 +54,15 @@ watch( annotationStore.resetAnnotations(); annotationStore.selectFilteredAnnotations(props.types); annotationStore.highlightTargetsLevel0(); + emit('init') }, { immediate: true }, ); -const unsubscribe = TextEventBus.on('click', ({ target }) => { +const unsubscribe = TextEventBus.on('click', ({ target }) => { - // Next we look up which annotations need to be selected + // Next we look up which annotations need to be selected let annotationIds = {}; Utils.getValuesFromAttribute(target, 'data-annotation-ids').forEach((value) => annotationIds[value] = true); diff --git a/src/components/annotations/variants/VariantsView.vue b/src/components/annotations/variants/VariantsView.vue index 0e009672..7af268d8 100644 --- a/src/components/annotations/variants/VariantsView.vue +++ b/src/components/annotations/variants/VariantsView.vue @@ -15,6 +15,8 @@ allocateWitnessColorInVariantItem() const annotations = computed(() => annotationStore.annotations); const activeContentUrl = computed(() => contentsStore.activeContentUrl); +const filteredAnnotations = computed(() => annotationStore.filteredAnnotations); + const updateTextHighlighting = computed(() => // We need to make sure that annotations are loaded (this.annotations), // the text HTML is present in DOM (this.activeContentUrl is set after DOM update) @@ -29,21 +31,36 @@ watch( annotationStore.resetAnnotations(); annotationStore.selectFilteredAnnotations([{ name: 'Variant' }]); annotationStore.highlightTargetsLevel0(); + annotationStore.isSingleSelectMode = false }, { immediate: true }, ); const unsubscribe = TextEventBus.on('click', ({ target }) => { - const targetIsSelected = parseInt(target.getAttribute('data-annotation-level'), 10) > 0; const ids = getAnnotationIdsFromTarget(target) + const annotations = filteredAnnotations.value.filter((filtered) => ids.find(id => filtered.id === id)) + if (!annotationStore.isSingleSelectMode) { + // We check if the found annotation ids are currently displayed in the active tab, if not we skip the handling + // the annotations referring to the target are not displayed - we do not proceed further + if (annotations.length === 0) return + } else { + // if we are in single select mode, we still have variant annotations, but there are not shown + // if we click at a part of text whose related annotations are not in the variant annotations, then we do not proceed further + const variantAnnotations = getVariantAnnotations(annotationStore.annotations, 'Variant') + if (!variantAnnotations.find((annotation) => annotation.id === ids[0])) { + return + } + } + + const targetIsSelected = parseInt(target.getAttribute('data-annotation-level'), 10) > 0 + if (annotationStore.isSingleSelectMode) { if (targetIsSelected) { annotationStore.removeFilteredAnnotations(ids) annotationStore.deactivateAnnotationsByIds(ids) - } - else { + } else { annotationStore.addFilteredAnnotations(ids) annotationStore.activateAnnotationsByIds(ids) } @@ -59,6 +76,14 @@ const unsubscribe = TextEventBus.on('click', ({ target }) => { onBeforeUnmount(() => unsubscribe()) +function getVariantAnnotations(annotations, type) { + let variantAnnotations = [] + annotations.forEach((annotation) => { + if(annotation.body['x-content-type'] === type) variantAnnotations.push(annotation) + }) + return variantAnnotations +} + function allocateWitnessColorInVariantItem() { const colors = {} if (!annotationStore.witnesses) return diff --git a/src/components/panels/Panel.vue b/src/components/panels/Panel.vue index ef33acea..f5b01065 100644 --- a/src/components/panels/Panel.vue +++ b/src/components/panels/Panel.vue @@ -139,7 +139,7 @@ import ContentView from '@/components/ContentView.vue'; import ImageView from '@/components/ImageView.vue'; import PanelZoomAction from '@/components/panels/actions/PanelZoomAction.vue'; import PanelCheckAction from '@/components/panels/actions/PanelCheckAction.vue'; -import PanelToggleAction from '@/components/panels/actions/PanelToggleAction.vue'; +import VariantsToggleModeAction from '@/components/panels/actions/VariantsToggleModeAction.vue'; import PanelImageAction from '@/components/panels/actions/PanelImageAction.vue'; import LoadingSpinner from '@/components/LoadingSpinner.vue'; import MessageBox from '@/components/MessageBox.vue'; @@ -161,7 +161,7 @@ export default { PanelImageAction, PanelCheckAction, PanelZoomAction, - PanelToggleAction, + VariantsToggleModeAction, TreeView, TabView, TabPanel, @@ -276,7 +276,7 @@ export default { if (!url) return; const selected = false; - const events = { + const actionEvents = { update: (value) => { if (value === null) return; if (value) annotationStore.selectAll(); @@ -284,11 +284,17 @@ export default { }, }; + const viewEvents = { + init: () => { + tabs.value[i].actions[0].props.selected = false + } + } + unsubscribe.value = annotationStore.$onAction(({ name, args, }) => { if (tabs.value.length - && tabs.value[0]?.actions?.length + && tabs.value[i]?.actions?.length && (name === 'setActiveAnnotations')) { const activeAnnotations = args[0]; const activeAmount = Object.keys(activeAnnotations).length; @@ -309,7 +315,7 @@ export default { selected, label: t('select_all'), }, - events, + events: actionEvents, }]; tabs.value = [...tabs.value, { @@ -317,32 +323,22 @@ export default { label, props: { ...connector.options }, actions, + events: viewEvents }]; } function createVariantsView(view) { - const annotationStore = useAnnotationsStore(); const { connector, label } = view; const { component } = findComponent(connector.id); - - const selectedSingleMode = false - const eventsSingleSelectMode = { - update: (value) => { - if (value) annotationStore.enableSingleSelectMode(); - else annotationStore.disableSingleSelectMode(); - }, - }; - const actions = [{ - component: 'PanelToggleAction', + component: 'VariantsToggleModeAction', props: { - selected: selectedSingleMode, + selected: false, label: t('single_select_mode'), }, - events: eventsSingleSelectMode, }]; - + tabs.value = [...tabs.value, { component, label, diff --git a/src/components/panels/actions/PanelToggleAction.vue b/src/components/panels/actions/VariantsToggleModeAction.vue similarity index 74% rename from src/components/panels/actions/PanelToggleAction.vue rename to src/components/panels/actions/VariantsToggleModeAction.vue index 7631c680..4c5b4cd7 100644 --- a/src/components/panels/actions/PanelToggleAction.vue +++ b/src/components/panels/actions/VariantsToggleModeAction.vue @@ -39,29 +39,36 @@ diff --git a/src/stores/annotations.ts b/src/stores/annotations.ts index 7d6d2d9c..833a4cc1 100644 --- a/src/stores/annotations.ts +++ b/src/stores/annotations.ts @@ -136,7 +136,7 @@ export const useAnnotationsStore = defineStore('annotations', () => { // When filtering by witness it can happen that a target is used for some other active annotation item, // In that case, we want to keep the level of highlighting it had and - + filteredAnnotations.value .filter(annotation => !activeIds.includes(annotation.id)) .forEach(annotation => { @@ -145,11 +145,10 @@ export const useAnnotationsStore = defineStore('annotations', () => { const selectorIsActive = activeIds.filter(id => selector === AnnotationUtils.generateTargetSelector(activeAnnotations.value[id])).length > 0; const target = document.querySelector(selector) if(!target) return; - + if (!selectorIsActive && AnnotationUtils.getCurrentLevel(target) < 0) { AnnotationUtils.highlightTargets(selector, {level: 0}); } - }) } @@ -301,7 +300,7 @@ export const useAnnotationsStore = defineStore('annotations', () => { const addHighlightClickListeners = () => { const textEl = document.querySelector('#text-content>div>*'); - + if (!textEl) return; textEl.addEventListener('click', ({target}) => { @@ -317,7 +316,7 @@ export const useAnnotationsStore = defineStore('annotations', () => { if (!target.dataset.annotation) { target = getNearestParentAnnotation(target); } - + if (!target) { return; } diff --git a/tests/cypress/e2e/variants.cy.js b/tests/cypress/e2e/variants.cy.js index e505cb31..b4636873 100644 --- a/tests/cypress/e2e/variants.cy.js +++ b/tests/cypress/e2e/variants.cy.js @@ -1,4 +1,4 @@ -import { commonSelectors, ahiqarSelectors, gflSelectors } from '../support/globals'; +import { commonSelectors, ahiqarSelectors } from '../support/globals'; const selectors = { ...commonSelectors, @@ -33,14 +33,14 @@ const selectors = { }) Cypress.Commands.add('clickSingleSelectButton', () => { - + cy .get('.panels-wrapper .panel:nth-child(4)') .find('.panel-header') .find('.actions') .children().eq(2) .find('input[type="checkbox"]') - .click() + .click() }) Cypress.Commands.add('checkNoAnnotationsAvailable', () => { @@ -53,9 +53,6 @@ const selectors = { .find('span').contains('No Annotations available') }) - - - describe('VariantsAnnotation', () => { beforeEach(() => { @@ -70,7 +67,7 @@ const selectors = { describe('Variants items selection', () => { it('Should display third annotation tab', () => { - cy + cy .get(selectors.tab) .children() .eq(2) @@ -80,14 +77,13 @@ const selectors = { }); it('Should show a list of variant items', () => { - cy + cy .get(selectors.list) .should('be.visible') .children() .should("have.length", 11) }); - it('select (unselect) a variant item', () => { // should select a variant item and add its witness after the highlighted text + the highlighted text should become light blue cy @@ -95,21 +91,21 @@ const selectors = { .children() .eq(0) .click() - .should('have.class', 'active') // the variant item is selected + .should('have.class', 'active') // the variant item is selected .get('div#MD12675N1l4l2l6l4l40') .find('span.witnesses') .find('span').contains('DFM 614') // the witness is added .parent() .next() .invoke('attr', 'data-annotation-level') - .should('eq', '1') // highlighted text should become light blue + .should('eq', '1') // highlighted text should become light blue // --- select sequentially another variant item --- .get(selectors.list) .children() .eq(1) .click() - .should('have.class', 'active') // the variant item is selected + .should('have.class', 'active') // the variant item is selected .get('div#MD12675N1l4l2l6l4l40') .find('span.witnesses') .find('span').contains('Ming. syr. 258') // the witness is added @@ -134,7 +130,7 @@ const selectors = { .next() .invoke('attr', 'data-annotation-level') .should('eq', '1') // highlighted text should stay light blue (we still have one witness) - + // --- unselect the second variant item .get(selectors.list) .children() @@ -153,14 +149,14 @@ const selectors = { describe('Witnesses', () => { it('Deselects a first witness from the dropdown', () => { cy - // click at one target - useful to see how this target's witnesses list change when we unclick at one witness in drop down + // click at one target - useful to see how this target's witnesses list change when we unclick at one witness in drop down .wait(500) .get('div#text-content div#MD12675N1l4l2l6l4l42') .children() .eq(1) .click() - - // click at the witness 'Cod. Arab. 236' of the drop down + + // click at the witness 'Cod. Arab. 236' of the drop down .clickWitnessItem('4 Witnesses selected', 'Cod. Arab. 236') // after this part we check the effects of this click @@ -222,7 +218,7 @@ const selectors = { .children() .should('have.length',4) .eq(0) - .checkTextInWitnessItemDescription('Cod. Arab. 236', 'test') + .checkTextInWitnessItemDescription('Cod. Arab. 236', 'test') // witness description will update once description is there .checkTextInWitnessItemDescription('DFM 614', 'test') .checkTextInWitnessItemDescription('Ming. syr. 258', 'test') @@ -264,7 +260,15 @@ const selectors = { .should('not.have.class', 'active') }) - // + it('should not select a target for a different annotation tab', () => { + cy + .get('#MD12675N1l4l2l6l4l58l2') + .click() + .should('have.attr', 'data-annotation-level', '-1') + .get(selectors.list) + .children() + .should('not.have.class', 'active') + }) it('should deselect the highlighted text, remove its witnesses and deselect all related variant items in variants tab', () => { // unclick at one target cy @@ -290,11 +294,10 @@ const selectors = { }) }) - describe('Single select mode', () => { it('should hide the variant items when single select mode is on', () => { - cy.wait(500).then(() => { + cy.wait(500).then(() => { // we wait till the text panel and annotations panel are fully loaded cy .clickSingleSelectButton().then(() => { @@ -305,18 +308,18 @@ const selectors = { it('should show variant items of the target as selected when clicking the target in single select mode', () => { - cy.wait(500).then(() => { + cy.wait(500).then(() => { cy .clickSingleSelectButton().then(() => { cy.clickTarget() - cy.get('.panels-wrapper .panel:nth-child(4) .panel-body div#pv_id_6_2_content') + cy.get('.panels-wrapper .panel:nth-child(4) .panel-body div#pv_id_6_2_content') .find('.annotations-list') .children().should('have.length', 4) .each(($li) => { expect($li).to.have.class('active') }) }) - }) + }) }) it('should hide the selected variant items after unclicking the target in single select mode', () => { @@ -331,7 +334,7 @@ const selectors = { .click() // we expect the selected annotations to be hidden from the tab cy.checkNoAnnotationsAvailable() - }) + }) }) it('should show again all the variant item when we switch off the single select mode', () => { @@ -340,12 +343,12 @@ const selectors = { .clickSingleSelectButton() // set the single select mode cy .clickSingleSelectButton() // switch off the single select mode - cy + cy .get(selectors.list) .should('be.visible') .children() - .should("have.length", 11) // we have 11 variant items as in the normal mode - no single select mode + .should("have.length", 11) // we have 11 variant items as in the normal mode - no single select mode }) }) - }) - }); \ No newline at end of file + }) + });