@internetarchive/ia-book-actions 0.0.5 → 0.0.6-4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/demo/index.html +52 -54
- package/package.json +1 -1
- package/src/assets/data/archive-logo.js +32 -0
- package/src/assets/data/lending-get-status-service.js +2 -337
- package/src/assets/styles/collapsible-action-group.js +28 -9
- package/src/components/book-title-bar.js +93 -0
- package/src/components/collapsible-action-group.js +25 -8
- package/src/core/config/analytics-event-and-category.js +4 -3
- package/src/core/services/actions-config.js +22 -10
- package/src/core/services/actions-handler/actions-handler-service.js +16 -22
- package/src/core/services/actions-handler/actions-handler.js +60 -10
- package/src/core/services/get-lending-actions.js +22 -31
- package/src/ia-book-actions.js +84 -19
- package/test/components/book-title-bar.test.js +39 -0
- package/test/components/collapsible-action-group.test.js +16 -0
- package/test/core/services/get-lending-actions.test.js +1 -1
- package/test/ia-book-actions.test.js +71 -31
package/src/ia-book-actions.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
/* eslint-disable camelcase */
|
|
1
2
|
import { html, css, LitElement } from 'lit-element';
|
|
2
3
|
|
|
3
4
|
import { SharedResizeObserver } from '@internetarchive/shared-resize-observer';
|
|
4
5
|
|
|
5
6
|
import './components/collapsible-action-group.js';
|
|
7
|
+
import './components/book-title-bar.js';
|
|
6
8
|
import './components/text-group.js';
|
|
7
9
|
import './components/info-icon.js';
|
|
8
10
|
|
|
@@ -14,10 +16,13 @@ export default class IABookActions extends LitElement {
|
|
|
14
16
|
return {
|
|
15
17
|
userid: { type: String },
|
|
16
18
|
identifier: { type: String },
|
|
19
|
+
bookTitle: { type: String },
|
|
17
20
|
lendingStatus: { type: Object },
|
|
18
21
|
width: { type: Number },
|
|
19
22
|
bwbPurchaseUrl: { type: String },
|
|
23
|
+
barType: { type: String },
|
|
20
24
|
sharedObserver: { attribute: false },
|
|
25
|
+
disable: { type: Boolean },
|
|
21
26
|
};
|
|
22
27
|
}
|
|
23
28
|
|
|
@@ -25,15 +30,18 @@ export default class IABookActions extends LitElement {
|
|
|
25
30
|
super();
|
|
26
31
|
this.userid = '';
|
|
27
32
|
this.identifier = '';
|
|
33
|
+
this.bookTitle = '';
|
|
28
34
|
this.lendingStatus = {};
|
|
35
|
+
this.width = 0;
|
|
36
|
+
this.bwbPurchaseUrl = '';
|
|
37
|
+
this.barType = 'action'; // 'title'|'action'
|
|
38
|
+
this.sharedObserver = undefined;
|
|
29
39
|
this.primaryActions = [];
|
|
30
40
|
this.primaryTitle = '';
|
|
31
41
|
this.primaryColor = 'primary';
|
|
32
42
|
this.secondaryActions = [];
|
|
33
|
-
this.
|
|
34
|
-
this.
|
|
35
|
-
this.lendingOptions = [];
|
|
36
|
-
this.sharedObserver = undefined;
|
|
43
|
+
this.lendingOptions = {};
|
|
44
|
+
this.disable = false;
|
|
37
45
|
}
|
|
38
46
|
|
|
39
47
|
disconnectedCallback() {
|
|
@@ -60,6 +68,27 @@ export default class IABookActions extends LitElement {
|
|
|
60
68
|
}
|
|
61
69
|
}
|
|
62
70
|
|
|
71
|
+
browseHasExpired() {
|
|
72
|
+
const currStatus = { ...this.lendingStatus, browsingExpired: true };
|
|
73
|
+
this.lendingStatus = currStatus;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
startBrowseTimer() {
|
|
77
|
+
const {
|
|
78
|
+
browsingExpired,
|
|
79
|
+
user_has_browsed,
|
|
80
|
+
secondsLeftOnLoan,
|
|
81
|
+
} = this.lendingStatus;
|
|
82
|
+
if (!user_has_browsed || browsingExpired) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const timeLeft = secondsLeftOnLoan * 1000;
|
|
87
|
+
setTimeout(() => {
|
|
88
|
+
this.browseHasExpired();
|
|
89
|
+
}, timeLeft);
|
|
90
|
+
}
|
|
91
|
+
|
|
63
92
|
/** SharedObserver resize handler */
|
|
64
93
|
handleResize(entry) {
|
|
65
94
|
// if you are observing multiple targets,
|
|
@@ -111,26 +140,63 @@ export default class IABookActions extends LitElement {
|
|
|
111
140
|
this.secondaryActions = actions.secondaryActions.filter(action => {
|
|
112
141
|
return action != null;
|
|
113
142
|
});
|
|
143
|
+
|
|
144
|
+
if (
|
|
145
|
+
this.lendingStatus.user_has_browsed &&
|
|
146
|
+
!this.lendingStatus.browseHasExpired
|
|
147
|
+
) {
|
|
148
|
+
this.startBrowseTimer();
|
|
149
|
+
}
|
|
114
150
|
}
|
|
115
151
|
|
|
116
152
|
render() {
|
|
117
153
|
return html`
|
|
118
154
|
<section class="lending-wrapper">
|
|
119
|
-
|
|
120
|
-
.userid=${this.userid}
|
|
121
|
-
.identifier=${this.identifier}
|
|
122
|
-
.primaryColor=${this.primaryColor}
|
|
123
|
-
.primaryActions=${this.primaryActions}
|
|
124
|
-
.secondaryActions=${this.secondaryActions}
|
|
125
|
-
.width=${this.width}
|
|
126
|
-
.hasAdminAccess=${this.hasAdminAccess}
|
|
127
|
-
>
|
|
128
|
-
</collapsible-action-group>
|
|
129
|
-
${this.textGroupTemplate} ${this.infoIconTemplate}
|
|
155
|
+
${this.barType === 'title' ? this.bookTitleBar : this.bookActionBar}
|
|
130
156
|
</section>
|
|
131
157
|
`;
|
|
132
158
|
}
|
|
133
159
|
|
|
160
|
+
get bookTitleBar() {
|
|
161
|
+
return html`<book-title-bar
|
|
162
|
+
.identifier=${this.identifier}
|
|
163
|
+
.bookTitle=${this.bookTitle}
|
|
164
|
+
></book-title-bar>`;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
get bookActionBar() {
|
|
168
|
+
return html`
|
|
169
|
+
<collapsible-action-group
|
|
170
|
+
.userid=${this.userid}
|
|
171
|
+
.identifier=${this.identifier}
|
|
172
|
+
.primaryColor=${this.primaryColor}
|
|
173
|
+
.primaryActions=${this.primaryActions}
|
|
174
|
+
.secondaryActions=${this.secondaryActions}
|
|
175
|
+
.width=${this.width}
|
|
176
|
+
.hasAdminAccess=${this.hasAdminAccess}
|
|
177
|
+
.disabled=${this.disable}
|
|
178
|
+
@toggle-action-bar-state=${this.toggleActionBarState}
|
|
179
|
+
>
|
|
180
|
+
</collapsible-action-group>
|
|
181
|
+
${this.textGroupTemplate} ${this.infoIconTemplate}
|
|
182
|
+
`;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
toggleActionBarState(e) {
|
|
186
|
+
console.log(e)
|
|
187
|
+
|
|
188
|
+
// update action bar states is book is not available to browse or borrow.
|
|
189
|
+
if (e?.detail?.data?.error) {
|
|
190
|
+
const errorMsg = e.detail.data.error;
|
|
191
|
+
if (errorMsg.match(/(^|\W)UNAVAILABLE($|\W)/)) {
|
|
192
|
+
const currStatus = { ...this.lendingStatus, available_to_browse: false };
|
|
193
|
+
this.lendingStatus = currStatus;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
this.disable = !this.disable;
|
|
198
|
+
}
|
|
199
|
+
|
|
134
200
|
get iconClass() {
|
|
135
201
|
return this.width <= mobileContainerWidth ? 'mobile' : 'desktop';
|
|
136
202
|
}
|
|
@@ -146,7 +212,7 @@ export default class IABookActions extends LitElement {
|
|
|
146
212
|
get textGroupTemplate() {
|
|
147
213
|
return html`<text-group
|
|
148
214
|
textClass=${this.textClass}
|
|
149
|
-
texts
|
|
215
|
+
texts=${this.primaryTitle}
|
|
150
216
|
>
|
|
151
217
|
</text-group>`;
|
|
152
218
|
}
|
|
@@ -159,13 +225,12 @@ export default class IABookActions extends LitElement {
|
|
|
159
225
|
return css`
|
|
160
226
|
:host {
|
|
161
227
|
display: block;
|
|
162
|
-
background: var(--
|
|
163
|
-
color: var(--primaryTextColor);
|
|
228
|
+
background: var(--primaryBGColor, #000);
|
|
229
|
+
color: var(--primaryTextColor, #fff);
|
|
164
230
|
}
|
|
165
231
|
.lending-wrapper {
|
|
166
232
|
width: 100%;
|
|
167
233
|
margin: 0 auto;
|
|
168
|
-
padding: 10px 0;
|
|
169
234
|
display: inline-flex;
|
|
170
235
|
align-items: center;
|
|
171
236
|
justify-content: center;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { html, fixture, expect, oneEvent } from '@open-wc/testing';
|
|
2
|
+
import '../../src/components/book-title-bar.js';
|
|
3
|
+
|
|
4
|
+
const container = ({ identifier, bookTitle } = {}) =>
|
|
5
|
+
html`<book-title-bar
|
|
6
|
+
.identifier=${identifier}
|
|
7
|
+
.bookTitle=${bookTitle}
|
|
8
|
+
></book-title-bar>`;
|
|
9
|
+
|
|
10
|
+
describe('<book-title-bar>', () => {
|
|
11
|
+
it('check inner texts of title bar', async () => {
|
|
12
|
+
const el = await fixture(
|
|
13
|
+
container({
|
|
14
|
+
identifier: 'goody',
|
|
15
|
+
bookTitle: 'this is test booktitle',
|
|
16
|
+
})
|
|
17
|
+
);
|
|
18
|
+
const link = el.shadowRoot.querySelector('a');
|
|
19
|
+
expect(link.innerText).to.equal('this is test booktitle');
|
|
20
|
+
expect(link.getAttribute('href')).to.equal('/details/goody');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('check if event if firing on valid analytics category action', async () => {
|
|
24
|
+
const el = await fixture(
|
|
25
|
+
container({
|
|
26
|
+
identifier: 'goody',
|
|
27
|
+
bookTitle: 'this is test booktitle',
|
|
28
|
+
})
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const clickButton = () =>
|
|
32
|
+
el.shadowRoot.querySelector('a').dispatchEvent(new Event('click'));
|
|
33
|
+
setTimeout(clickButton);
|
|
34
|
+
|
|
35
|
+
const { detail } = await oneEvent(el, 'bookTitleBar');
|
|
36
|
+
expect(detail.event.category).to.equal('BookReader-Header');
|
|
37
|
+
expect(detail.event.action).to.equal('Book-Title-Bar');
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -39,6 +39,22 @@ describe('<collapsible-action-group>', () => {
|
|
|
39
39
|
const primaryButton = primaryActionContainer.querySelector('.ia-button');
|
|
40
40
|
expect(primaryButton.innerText).to.equal('Borrow for 1 hour');
|
|
41
41
|
});
|
|
42
|
+
|
|
43
|
+
it('check if loader is active and action-group is disabled', async () => {
|
|
44
|
+
const el = await fixture(container());
|
|
45
|
+
|
|
46
|
+
const primaryActionContainer = el.shadowRoot.querySelector('.primary');
|
|
47
|
+
primaryActionContainer
|
|
48
|
+
.querySelector('.ia-button')
|
|
49
|
+
.dispatchEvent(new CustomEvent('toggle-loader'));
|
|
50
|
+
el.disabled = true;
|
|
51
|
+
await el.updateComplete;
|
|
52
|
+
|
|
53
|
+
expect(el.disabled).to.be.true;
|
|
54
|
+
expect(el.shadowRoot.querySelector('div').classList.contains('disabled')).to
|
|
55
|
+
.be.true;
|
|
56
|
+
});
|
|
57
|
+
|
|
42
58
|
it('fires analytics events', async () => {
|
|
43
59
|
const el = await fixture(container());
|
|
44
60
|
const primaryActionContainer = el.shadowRoot.querySelector('.primary');
|
|
@@ -36,7 +36,7 @@ describe('Get Lending Actions', () => {
|
|
|
36
36
|
);
|
|
37
37
|
const actions = lendingOptions.getCurrentLendingActions();
|
|
38
38
|
|
|
39
|
-
expect(actions.primaryActions.length).to.equal(
|
|
39
|
+
expect(actions.primaryActions.length).to.equal(2);
|
|
40
40
|
expect(actions.primaryTitle).to.equal(
|
|
41
41
|
'This book can be borrowed for 14 days.'
|
|
42
42
|
);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { html, fixture, expect } from '@open-wc/testing';
|
|
1
|
+
import { html, fixture, expect, aTimeout } from '@open-wc/testing';
|
|
2
|
+
|
|
2
3
|
import '../src/ia-book-actions.js';
|
|
3
4
|
import Sinon from 'sinon';
|
|
4
5
|
|
|
@@ -126,7 +127,7 @@ describe('Borrow status actions', () => {
|
|
|
126
127
|
expect(el.primaryTitle).to.equal(
|
|
127
128
|
'Renewable every hour, pending availability.'
|
|
128
129
|
);
|
|
129
|
-
expect(el.primaryActions.length).to.equal(
|
|
130
|
+
expect(el.primaryActions.length).to.equal(2);
|
|
130
131
|
expect(el.primaryActions[0].text).to.equal('Log In and Borrow');
|
|
131
132
|
expect(el.primaryActions[0].text).to.equal(expectedPrimaryActions[0].text);
|
|
132
133
|
});
|
|
@@ -159,40 +160,79 @@ describe('Borrow status actions', () => {
|
|
|
159
160
|
expect(el.primaryActions[0].text).to.equal(expectedPrimaryActions[0].text);
|
|
160
161
|
expect(el.primaryActions[1].text).to.equal('Borrow for 14 days');
|
|
161
162
|
});
|
|
163
|
+
});
|
|
162
164
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
.identifier=${'foo'}
|
|
170
|
-
.lendingStatus=${{
|
|
165
|
+
describe('Browsing expired status', () => {
|
|
166
|
+
it('Book is browsed but not expired', async () => {
|
|
167
|
+
const el = await fixture(
|
|
168
|
+
container({
|
|
169
|
+
userid: '@userid',
|
|
170
|
+
lendingStatus: {
|
|
171
171
|
is_lendable: true,
|
|
172
172
|
user_has_browsed: true,
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
173
|
+
browseHasExpired: false,
|
|
174
|
+
},
|
|
175
|
+
})
|
|
176
|
+
);
|
|
177
|
+
expect(el.primaryTitle).contains('Borrow ends at ');
|
|
178
|
+
expect(el.primaryActions[0].text).to.equal('Return now');
|
|
179
|
+
expect(el.primaryActions[1].text).to.equal('Print Disability Access');
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('Book is browsing and going to expire after 1 second', async () => {
|
|
183
|
+
const el = await fixture(
|
|
184
|
+
container({
|
|
185
|
+
userid: '@userid',
|
|
186
|
+
lendingStatus: {
|
|
187
187
|
is_lendable: true,
|
|
188
188
|
user_has_browsed: true,
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
|
|
189
|
+
browseHasExpired: false,
|
|
190
|
+
secondsLeftOnLoan: 1,
|
|
191
|
+
},
|
|
192
|
+
})
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
await aTimeout(1500); // wait for 1.5 second
|
|
196
|
+
el.updateComplete;
|
|
197
|
+
|
|
198
|
+
expect(el.primaryTitle).contains('Your loan has expired.');
|
|
199
|
+
expect(el.primaryActions[0].text).to.equal('Borrow again');
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
describe('Shared Resize Observer', () => {
|
|
204
|
+
it('can receive a Shared Resize Observer', async () => {
|
|
205
|
+
const sharedObserverStub = new SharedResizeObserver();
|
|
206
|
+
const addObserverSpy = Sinon.spy(sharedObserverStub, 'addObserver');
|
|
207
|
+
const component = await fixture(html` <ia-book-actions
|
|
208
|
+
.userid=${'@userid'}
|
|
209
|
+
.identifier=${'foo'}
|
|
210
|
+
.lendingStatus=${{
|
|
211
|
+
is_lendable: true,
|
|
212
|
+
user_has_browsed: true,
|
|
213
|
+
available_to_browse: true,
|
|
214
|
+
available_to_borrow: true,
|
|
215
|
+
}}
|
|
216
|
+
.sharedObserver=${sharedObserverStub}
|
|
217
|
+
></ia-book-actions>`);
|
|
218
|
+
await component.updateComplete;
|
|
219
|
+
|
|
220
|
+
expect(addObserverSpy.callCount).to.equal(1);
|
|
221
|
+
});
|
|
193
222
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
223
|
+
it('loads its own resize observer if it is not received', async () => {
|
|
224
|
+
const component = await fixture(html` <ia-book-actions
|
|
225
|
+
.userid=${'@userid'}
|
|
226
|
+
.identifier=${'foo'}
|
|
227
|
+
.lendingStatus=${{
|
|
228
|
+
is_lendable: true,
|
|
229
|
+
user_has_browsed: true,
|
|
230
|
+
available_to_browse: true,
|
|
231
|
+
available_to_borrow: true,
|
|
232
|
+
}}
|
|
233
|
+
></ia-book-actions>`);
|
|
234
|
+
|
|
235
|
+
await component.updateComplete;
|
|
236
|
+
expect(component.sharedObserver).to.not.be.undefined;
|
|
197
237
|
});
|
|
198
238
|
});
|