Skip to content

Commit

Permalink
Merge pull request #394 from pulibrary/i391-banner-dismiss
Browse files Browse the repository at this point in the history
[#391] Remember when the user has dismissed the banner
  • Loading branch information
christinach authored Jul 23, 2024
2 parents 39e7e21 + bbeb3e5 commit e017273
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 23 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"@honeybadger-io/vue": "^6.1.7",
"axios": "^1.5.1",
"class-transformer": "^0.5.1",
"lux-design-system": "^5.2.15",
"lux-design-system": "^5.3.0",
"typescript-eslint": "^7.8.0",
"vue": "^3.3.4"
},
Expand Down
114 changes: 101 additions & 13 deletions src/components/BannerAlert.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { VueWrapper, mount, flushPromises } from '@vue/test-utils';
import { describe, expect, it, SpyInstance, vi } from 'vitest';
import {
afterEach,
beforeEach,
describe,
expect,
it,
SpyInstance,
vi
} from 'vitest';
import { BannerResult } from '../models/BannerResult';
import { BannerService } from '../services/BannerService';
import BannerAlert from './BannerAlert.vue';
Expand Down Expand Up @@ -62,19 +70,99 @@ describe('Banner', () => {
expect(wrapper.find('div.lux-alert-error').exists()).toBe(true);
});

it('sets the dismissible property', async () => {
mock = vi.spyOn(BannerService.prototype, 'result');
testResult = new BannerResult('This is an error', true, 'info', true, true);
mock.mockResolvedValue(testResult);
wrapper = mount(BannerAlert, {
props: {
resultsPromise: new BannerService().result()
}
describe('when the result is dismissible', () => {
beforeEach(() => {
mock = vi.spyOn(BannerService.prototype, 'result');
testResult = new BannerResult(
'This is an error',
true,
'info',
true,
true
);
mock.mockResolvedValue(testResult);
vi.useFakeTimers();
});
afterEach(() => {
vi.useRealTimers();
});
it('sets the dismissible property', async () => {
wrapper = mount(BannerAlert, {
props: {
resultsPromise: new BannerService().result()
}
});
await flushPromises();
expect(wrapper.find('div.lux-alert').exists()).toBe(true);
expect(wrapper.find('div.lux-alert-info').exists()).toBe(true);
expect(wrapper.find('div.lux-alert-dismissible').exists()).toBe(true);
});
it('records the banner text and date in localStorage when user dismisses it', async () => {
wrapper = mount(BannerAlert, {
props: {
resultsPromise: new BannerService().result()
}
});
await flushPromises();
const setItem = vi.spyOn(window.localStorage, 'setItem');
vi.setSystemTime(new Date(2030, 0, 1)); // January 1, 2030
wrapper.get('button.lux-close').trigger('click');

await flushPromises();
expect(wrapper.find('div.lux-alert').exists()).toBe(true);
expect(wrapper.find('div.lux-alert-info').exists()).toBe(true);
expect(wrapper.find('div.lux-alert-dismissible').exists()).toBe(true);
expect(setItem).toHaveBeenCalledWith(
'allsearch-banner-dismissed',
'{"date":"2030-01-01","text":"This is an error"}'
);
});

it('does not display the banner if it has been dismissed in the past week', async () => {
const getItem = vi.spyOn(window.localStorage, 'getItem');
getItem.mockReturnValue(
'{"date":"2030-01-01","text":"This is an error"}'
);
vi.setSystemTime(new Date(2030, 0, 5)); // January 5, 2030

wrapper = mount(BannerAlert, {
props: {
resultsPromise: new BannerService().result()
}
});
await flushPromises();

expect(wrapper.find('div.lux-alert').exists()).toBe(false);
});

it('does display the banner if it was been dismissed a long time ago', async () => {
const getItem = vi.spyOn(window.localStorage, 'getItem');
getItem.mockReturnValue(
'{"date":"2030-01-01","text":"This is an error"}'
);
vi.setSystemTime(new Date(2035, 6, 20)); // July 20, 2035

wrapper = mount(BannerAlert, {
props: {
resultsPromise: new BannerService().result()
}
});
await flushPromises();

expect(wrapper.find('div.lux-alert').exists()).toBe(true);
});

it('does display the banner if the text is different than what was previously dismissed', async () => {
const getItem = vi.spyOn(window.localStorage, 'getItem');
getItem.mockReturnValue(
'{"date":"2030-01-01","text":"This text is outdated"}'
);
vi.setSystemTime(new Date(2030, 0, 5)); // January 5, 2030

wrapper = mount(BannerAlert, {
props: {
resultsPromise: new BannerService().result()
}
});
await flushPromises();

expect(wrapper.find('div.lux-alert').exists()).toBe(true);
});
});
});
45 changes: 40 additions & 5 deletions src/components/BannerAlert.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<template>
<lux-alert
v-if="result?.display_banner"
:status="result.alert_status"
:autoclear="result.autoclear"
:dismissible="result.dismissible"
v-if="shouldDisplayBanner()"
:status="result?.alert_status"
:autoclear="result?.autoclear"
:dismissible="result?.dismissible"
@dismissed="handleDismissed"
>
<!-- nosemgrep javascript.vue.security.audit.xss.templates.avoid-v-html.avoid-v-html -->
<span v-html="result.text"></span>
<span v-html="result?.text"></span>
</lux-alert>
</template>
<script setup lang="ts">
Expand All @@ -23,5 +24,39 @@ async function populateResult(): Promise<void> {
result.value = await bannerPromise;
}
function handleDismissed() {
window.localStorage.setItem(
'allsearch-banner-dismissed',
JSON.stringify({
date: new Date().toISOString().split('T')[0],
text: result.value?.text
})
);
}
function shouldDisplayBanner(): boolean {
if (!result.value) {
return false;
}
const lastDismissed = window.localStorage.getItem(
'allsearch-banner-dismissed'
);
if (lastDismissed) {
const parsed = JSON.parse(lastDismissed);
const lastDismissedDate = new Date(parsed.date);
if (isRecent(lastDismissedDate) && parsed.text === result.value?.text) {
return false;
}
}
return result.value?.display_banner || false;
}
function isRecent(date: Date): boolean {
var cutoffDate = new Date();
var sevenDaysAgo = cutoffDate.getDate() - 7;
cutoffDate.setDate(sevenDaysAgo);
return date > cutoffDate;
}
populateResult();
</script>
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1716,10 +1716,10 @@ lru-cache@^10.2.0:
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.2.tgz#48206bc114c1252940c41b25b41af5b545aca878"
integrity sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==

lux-design-system@^5.2.15:
version "5.2.15"
resolved "https://registry.yarnpkg.com/lux-design-system/-/lux-design-system-5.2.15.tgz#ae55558dc57fbea365b83b1d6dfe5f0e0c6a464e"
integrity sha512-b7KTB5mjWdZp7rfis4uO3NjV/oYFinlziitaiiXZpvZ7cqLanNgFt6cLvJLfolOjUQ/XvmKV3FNACDSyjBeeDQ==
lux-design-system@^5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/lux-design-system/-/lux-design-system-5.3.0.tgz#17388c4ae3e85920291e7be8794e6b0f2fd8db61"
integrity sha512-nvUQQmqrV3713qYJsfdqDQICoUjeIFHB0GpEI4q4EX4Obvb0OXriuBxs/4qhNxrBQHCQzzwIv1BE+Y+gpX0zrw==
dependencies:
core-js "^3.8.3"
register-service-worker "^1.7.2"
Expand Down

0 comments on commit e017273

Please sign in to comment.