@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.
@@ -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.width = 0;
34
- this.bwbPurchaseUrl = '';
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
- <collapsible-action-group
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="${this.primaryTitle}"
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(--secondaryBGColor);
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(1);
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(1);
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
- describe('Shared Resize Observer', () => {
164
- it('can receive a Shared Resize Observer', async () => {
165
- const sharedObserverStub = new SharedResizeObserver();
166
- const addObserverSpy = Sinon.spy(sharedObserverStub, 'addObserver');
167
- const component = await fixture(html` <ia-book-actions
168
- .userid=${'@userid'}
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
- available_to_browse: true,
174
- available_to_borrow: true,
175
- }}
176
- .sharedObserver=${sharedObserverStub}
177
- ></ia-book-actions>`);
178
- await component.updateComplete;
179
-
180
- expect(addObserverSpy.callCount).to.equal(1);
181
- });
182
- it('loads its own resize observer if it is not received', async () => {
183
- const component = await fixture(html` <ia-book-actions
184
- .userid=${'@userid'}
185
- .identifier=${'foo'}
186
- .lendingStatus=${{
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
- available_to_browse: true,
190
- available_to_borrow: true,
191
- }}
192
- ></ia-book-actions>`);
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
- await component.updateComplete;
195
- expect(component.sharedObserver).to.not.be.undefined;
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
  });