Skip to content

Commit

Permalink
Feature #847: Add close button to QR popover (#1104)
Browse files Browse the repository at this point in the history
* Hide popover on click of a button that has data-closes-popover attribut
* Hide popover on escape key as well
  • Loading branch information
sadiqkhoja authored Dec 14, 2024
1 parent 6464c2d commit 59cc444
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 4 deletions.
2 changes: 1 addition & 1 deletion src/components/field-key/new.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ except according to the terms contained in the LICENSE file.
<span>{{ $t('success[0]', created) }}</span>
</p>
</div>
<field-key-qr-panel :field-key="created" :managed="managed"/>
<field-key-qr-panel :field-key="created" :managed="managed" :show-close="false"/>
<p>{{ $t('success[1]', created) }}</p>
<i18n-t tag="p" keypath="success[2].full">
<template #formAccessSettings>
Expand Down
10 changes: 8 additions & 2 deletions src/components/popover.vue
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,13 @@ export default {
// not show, then hide), we use event capturing here.
document.addEventListener('click', this.hideAfterClick, true);
window.addEventListener('resize', this.hideAfterResize);
document.addEventListener('keydown', this.hideAfterEsc);
},
beforeUnmount() {
if (this.target != null) this.hide();
document.removeEventListener('click', this.hideAfterClick, true);
window.removeEventListener('resize', this.hideAfterResize);
document.removeEventListener('keydown', this.hideAfterEsc);
},
methods: {
show() {
Expand Down Expand Up @@ -108,12 +110,16 @@ export default {
this.show();
},
hideAfterClick(event) {
if (this.target != null && event.target.closest('.popover') == null &&
!this.target.contains(event.target))
if (this.target != null &&
((event.target.closest('.popover') == null && !this.target.contains(event.target)) ||
(event.target.hasAttribute('data-closes-popover'))))
this.$emit('hide');
},
hideAfterResize() {
if (this.target != null) this.$emit('hide');
},
hideAfterEsc({ key }) {
if (this.target != null && key === 'Escape') this.$emit('hide');
}
}
};
Expand Down
18 changes: 17 additions & 1 deletion src/components/qr-panel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,25 @@ except according to the terms contained in the LICENSE file.
<h1 class="panel-title"><slot name="title"></slot></h1>
<span class="icon-mobile"></span>
</div>
<div class="panel-body"><slot name="body"></slot></div>
<div class="panel-body">
<slot name="body"></slot>
<div v-if="showClose" class="panel-footer">
<button type="button" class="btn btn-primary" data-closes-popover>{{ $t('action.close') }}</button>
</div>
</div>
</div>
</template>

<script setup>
defineOptions({
name: 'QrPanel'
});
defineProps({
showClose: {
type: Boolean,
default: true
}
});
</script>

<style lang="scss">
Expand All @@ -37,6 +48,11 @@ defineOptions({
position: relative;
}

.panel-footer {
margin-bottom: -15px;
border-radius: 0;
}

// The icon is not animated, because in FieldKeyQrPanel, switching between a
// managed and a legacy QR code would reset the animation.
.icon-mobile {
Expand Down
8 changes: 8 additions & 0 deletions test/components/field-key/row.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ describe('FieldKeyRow', () => {
should.not.exist(document.querySelector('.popover'));
});

it('hides the popover on close button', async () => {
const app = await load('/projects/1/app-users', { attachTo: document.body });
await app.get('.field-key-row-popover-link').trigger('click');
should.exist(document.querySelector('.popover .field-key-qr-panel'));
await document.querySelector('.popover button').click();
should.not.exist(document.querySelector('.popover'));
});

it("shows the app user's display name", async () => {
const app = await load('/projects/1/app-users', { attachTo: document.body });
await app.get('.field-key-row-popover-link').trigger('click');
Expand Down
13 changes: 13 additions & 0 deletions test/components/form-draft/testing.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,19 @@ describe('FormDraftTesting', () => {
should.not.exist(document.querySelector('.popover'));
});

it('hides QR code on close button', async () => {
mockLogin();
testData.extendedForms.createPast(1, { draft: true });
const component = await load('/projects/1/forms/f/draft/testing', {
root: false,
attachTo: document.body
});
await component.get('#submission-list-test-on-device').trigger('click');
should.exist(document.querySelector('.popover .form-draft-qr-panel'));
await document.querySelector('.popover button').click();
should.not.exist(document.querySelector('.popover'));
});

describe('submission count', () => {
beforeEach(mockLogin);

Expand Down
22 changes: 22 additions & 0 deletions test/components/popover.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,28 @@ describe('Popover', () => {
document.querySelectorAll('.popover').length.should.equal(0);
});

it('hides the popover after close button of popover is clicked', async () => {
const component = mount(TestUtilPopoverLinks, { attachTo: document.body });
document.querySelector('#show-foo').click();
await component.vm.$nextTick();
await component.vm.$nextTick();
document.querySelectorAll('.popover').length.should.equal(1);
document.querySelector('.popover button').click();
await component.vm.$nextTick();
document.querySelectorAll('.popover').length.should.equal(0);
});

it('hides on escape', async () => {
const component = mount(TestUtilPopoverLinks, { attachTo: document.body });
document.querySelector('#show-foo').click();
await component.vm.$nextTick();
await component.vm.$nextTick();
document.querySelectorAll('.popover').length.should.equal(1);
await component.trigger('keydown', { key: 'Escape' });
await component.vm.$nextTick();
document.querySelectorAll('.popover').length.should.equal(0);
});

it('does not hide the popover after a click on the popover', async () => {
const component = mount(TestUtilPopoverLinks, { attachTo: document.body });
document.querySelector('#show-foo').click();
Expand Down
1 change: 1 addition & 0 deletions test/util/components/popover-links.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

<popover :target="popover.target" placement="left" @hide="hide">
<p>{{ popover.text }}</p>
<button type="button" data-closes-popover>close</button>
</popover>
</div>
</template>
Expand Down

0 comments on commit 59cc444

Please sign in to comment.