@internetarchive/ia-book-actions 0.0.6-1 → 0.0.6-5

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 CHANGED
@@ -96,7 +96,7 @@
96
96
  iaBookActions.userid = '';
97
97
  iaBookActions.identifier = identifier;
98
98
  iaBookActions.bookTitle = 'Contemporary Black biography. Volume 39 : profiles from the interContemporary Black biography. Volume 39';
99
- iaBookActions.lendingStatus = defaultLendingStatus;
99
+ iaBookActions.lendingStatus = canBrowseAndBorrow;
100
100
  iaBookActions.bwbPurchaseUrl = '';
101
101
  iaBookActions.barType = '';
102
102
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@internetarchive/ia-book-actions",
3
- "version": "0.0.6-1",
3
+ "version": "0.0.6-5",
4
4
  "description": "Bookreader actions for borrowable book",
5
5
  "author": "Internet Archive",
6
6
  "license": "AGPL-3.0-only",
@@ -80,7 +80,7 @@ export default css`
80
80
  padding: 0 0.6rem;
81
81
  margin-left: 0;
82
82
  }
83
- .action-loader {
83
+ .actionloader {
84
84
  vertical-align: middle;
85
85
  visibility: hidden;
86
86
  padding: 0.9rem 0.2rem;
@@ -129,9 +129,14 @@ export default css`
129
129
  display: initial;
130
130
  font-size: 1.4rem;
131
131
  }
132
-
133
132
  .unavailable {
134
133
  opacity: 0.7;
135
134
  pointer-events: none;
136
135
  }
136
+
137
+ .disabled {
138
+ opacity: 0.8;
139
+ pointer-events: none;
140
+ visibility: visible;
141
+ }
137
142
  `;
@@ -1,5 +1,6 @@
1
1
  import { html } from 'lit-element';
2
2
  import { nothing } from 'lit-html';
3
+ import { classMap } from 'lit-html/directives/class-map';
3
4
 
4
5
  import ActionsHandler from '../core/services/actions-handler/actions-handler.js';
5
6
 
@@ -25,6 +26,7 @@ export class CollapsibleActionGroup extends ActionsHandler {
25
26
  width: { type: Number },
26
27
  hasAdminAccess: { type: Boolean },
27
28
  dropdownArrow: { type: String },
29
+ disabled: { type: Boolean },
28
30
  };
29
31
  }
30
32
 
@@ -42,6 +44,7 @@ export class CollapsibleActionGroup extends ActionsHandler {
42
44
  this.initialButton = false;
43
45
  this.title = '';
44
46
  this.loaderIcon = 'https://archive.org/upload/images/tree/loading.gif';
47
+ this.disabled = false;
45
48
  }
46
49
 
47
50
  updated(changed) {
@@ -94,13 +97,20 @@ export class CollapsibleActionGroup extends ActionsHandler {
94
97
 
95
98
  render() {
96
99
  return html`
97
- ${this.getLoaderIcon}
98
- <section class="action-buttons primary">
99
- ${this.renderPrimaryActions}
100
- </section>
101
- <section class="action-buttons secondary">
102
- ${this.renderSecondaryActions}
103
- </section>
100
+ <div
101
+ class="${classMap({
102
+ actiongroup: true,
103
+ disabled: this.disabled,
104
+ })}"
105
+ >
106
+ ${this.getLoaderIcon}
107
+ <section class="action-buttons primary">
108
+ ${this.renderPrimaryActions}
109
+ </section>
110
+ <section class="action-buttons secondary">
111
+ ${this.renderSecondaryActions}
112
+ </section>
113
+ </div>
104
114
  `;
105
115
  }
106
116
 
@@ -225,7 +235,14 @@ export class CollapsibleActionGroup extends ActionsHandler {
225
235
  * @returns { HTMLElement }
226
236
  */
227
237
  get getLoaderIcon() {
228
- return html`<img class="action-loader" alt="" src="${this.loaderIcon}" />`;
238
+ return html`<img
239
+ class="${classMap({
240
+ actionloader: true,
241
+ disabled: this.disabled,
242
+ })}"
243
+ alt=""
244
+ src="${this.loaderIcon}"
245
+ />`;
229
246
  }
230
247
 
231
248
  /**
@@ -10,19 +10,15 @@ export default function ActionsHandlerService(options) {
10
10
  identifier: '',
11
11
  data: {},
12
12
  success() {},
13
- error: null,
13
+ error() {},
14
14
  loader: true,
15
15
  type: 'POST',
16
16
  ...options,
17
17
  };
18
18
 
19
- if (option.loader) {
20
- showActionButtonLoader();
21
- }
22
-
23
19
  let baseHost = '';
24
20
  if (window.location.pathname === '/demo/') {
25
- baseHost = `/demo/`;
21
+ baseHost = `/demo/1`;
26
22
  } else {
27
23
  baseHost = `/services/loans/loan`;
28
24
  }
@@ -36,23 +32,25 @@ export default function ActionsHandlerService(options) {
36
32
  body: formData,
37
33
  })
38
34
  .then(response => {
39
- if (response.status === 200) {
40
- option.success.call();
35
+ // test changes, (won't affect you)
36
+ if (baseHost == '/demo/1' || baseHost == '/demo/') {
37
+ return {
38
+ error: 'This book is not available to browse at this time. Please try again later.',
39
+ // error: 'This book is not available to borrow at this time. Please try again later.',
40
+ };
41
41
  }
42
+
43
+ // The response is a Response instance.
44
+ // You parse the data into a useable format using `.json()`
45
+ return response.json();
42
46
  })
43
- .catch(error => {
44
- console.log(error);
47
+ .then(data => {
48
+ // `data` is the parsed version of the JSON returned from the above endpoint.
49
+ console.log('actual reponse for endpoint', data);
50
+ if (!data.error) {
51
+ option?.success(data);
52
+ } else {
53
+ option?.error(data);
54
+ }
45
55
  });
46
56
  }
47
-
48
- function showActionButtonLoader() {
49
- const collapsibleElement = document
50
- .querySelector('ia-book-actions')
51
- .shadowRoot.querySelector('collapsible-action-group');
52
- collapsibleElement.setAttribute('style', 'opacity:0.8; pointer-events:none');
53
-
54
- const actionLoader = collapsibleElement.shadowRoot.querySelector(
55
- '.action-loader'
56
- );
57
- actionLoader.setAttribute('style', 'visibility: visible');
58
- }
@@ -7,10 +7,7 @@ import ActionsHandlerService from './actions-handler-service.js';
7
7
  *
8
8
  * ActionsHandlerService is a function being used to execute
9
9
  * APIs based of the request made by user. It consist some parameters are as follows:-
10
- * 1. action: action name like browse_book, return_book, borrow_book etc...
11
- * 2. identifier: book name.
12
- * 3. success: This is callback function which will be executed after the API request
13
- * return reponse as a success.
10
+ * 1. identifier: book name.
14
11
  */
15
12
 
16
13
  export default class ActionsHandler extends LitElement {
@@ -90,53 +87,88 @@ export default class ActionsHandler extends LitElement {
90
87
  }
91
88
 
92
89
  handleBrowseIt() {
90
+ const context = 'browse_book';
91
+ this.toggleActionBarState(context);
92
+
93
93
  ActionsHandlerService({
94
- action: 'browse_book',
94
+ action: context,
95
95
  identifier: this.identifier,
96
96
  success: () => {
97
97
  this.handleReadItNow();
98
98
  },
99
+ error: data => {
100
+ alert(data.error);
101
+ this.toggleActionBarState(context, data);
102
+ },
99
103
  });
100
104
  }
101
105
 
102
106
  handleReturnIt() {
107
+ const context = 'return_loan';
108
+ this.toggleActionBarState(context);
109
+
103
110
  ActionsHandlerService({
104
- action: 'return_loan',
111
+ action: context,
105
112
  identifier: this.identifier,
106
113
  success: () => {
107
114
  this.deleteLoanCookies();
108
115
  URLHelper.goToUrl(`/details/${this.identifier}`, true);
109
116
  },
117
+ error: data => {
118
+ alert(data.error);
119
+ this.toggleActionBarState(context);
120
+ },
110
121
  });
111
122
  }
112
123
 
113
124
  handleBorrowIt() {
125
+ const context = 'borrow_book';
126
+ this.toggleActionBarState(context);
127
+
114
128
  ActionsHandlerService({
115
- action: 'borrow_book',
129
+ action: context,
116
130
  identifier: this.identifier,
117
131
  success: () => {
118
132
  this.handleReadItNow();
119
133
  },
134
+ error: data => {
135
+ alert(data.error);
136
+ this.toggleActionBarState(context, data);
137
+ },
120
138
  });
121
139
  }
122
140
 
123
141
  handleReserveIt() {
142
+ const context = 'join_waitlist';
143
+ this.toggleActionBarState(context);
144
+
124
145
  ActionsHandlerService({
125
- action: 'join_waitlist',
146
+ action: context,
126
147
  identifier: this.identifier,
127
148
  success: () => {
128
149
  URLHelper.goToUrl(URLHelper.getRedirectUrl(), true);
129
150
  },
151
+ error: data => {
152
+ alert(data.error);
153
+ this.toggleActionBarState(context);
154
+ },
130
155
  });
131
156
  }
132
157
 
133
158
  handleRemoveFromWaitingList() {
159
+ const context = 'leave_waitlist';
160
+ this.toggleActionBarState(context);
161
+
134
162
  ActionsHandlerService({
135
- action: 'leave_waitlist',
163
+ action: context,
136
164
  identifier: this.identifier,
137
165
  success: () => {
138
166
  URLHelper.goToUrl(URLHelper.getRedirectUrl(), true);
139
167
  },
168
+ error: data => {
169
+ alert(data.error);
170
+ this.toggleActionBarState(context);
171
+ },
140
172
  });
141
173
  }
142
174
 
@@ -151,6 +183,9 @@ export default class ActionsHandler extends LitElement {
151
183
  handleReadItNow(extraParam) {
152
184
  const currentParams = new URLSearchParams(window.location.search);
153
185
 
186
+ // Delete the ?q= parameter just after borrow a book. see: WEBDEV-3979
187
+ currentParams.delete('q');
188
+
154
189
  if (extraParam) {
155
190
  // append extraParam key-value in currentParams
156
191
  const extraParams = new URLSearchParams(extraParam);
@@ -186,4 +221,12 @@ export default class ActionsHandler extends LitElement {
186
221
  cookie += '; path=/; domain=.archive.org;';
187
222
  document.cookie = cookie;
188
223
  }
224
+
225
+ toggleActionBarState(context, data = {}) {
226
+ this.dispatchEvent(
227
+ new CustomEvent('toggle-action-bar-state', {
228
+ detail: { context, data },
229
+ })
230
+ );
231
+ }
189
232
  }
@@ -22,6 +22,7 @@ export default class IABookActions extends LitElement {
22
22
  bwbPurchaseUrl: { type: String },
23
23
  barType: { type: String },
24
24
  sharedObserver: { attribute: false },
25
+ disable: { type: Boolean },
25
26
  };
26
27
  }
27
28
 
@@ -40,6 +41,7 @@ export default class IABookActions extends LitElement {
40
41
  this.primaryColor = 'primary';
41
42
  this.secondaryActions = [];
42
43
  this.lendingOptions = {};
44
+ this.disable = false;
43
45
  }
44
46
 
45
47
  disconnectedCallback() {
@@ -67,8 +69,7 @@ export default class IABookActions extends LitElement {
67
69
  }
68
70
 
69
71
  browseHasExpired() {
70
- const browsingExpired = true;
71
- const currStatus = { ...this.lendingStatus, browsingExpired };
72
+ const currStatus = { ...this.lendingStatus, browsingExpired: true };
72
73
  this.lendingStatus = currStatus;
73
74
  }
74
75
 
@@ -173,12 +174,31 @@ export default class IABookActions extends LitElement {
173
174
  .secondaryActions=${this.secondaryActions}
174
175
  .width=${this.width}
175
176
  .hasAdminAccess=${this.hasAdminAccess}
177
+ .disabled=${this.disable}
178
+ @toggle-action-bar-state=${this.toggleActionBarState}
176
179
  >
177
180
  </collapsible-action-group>
178
181
  ${this.textGroupTemplate} ${this.infoIconTemplate}
179
182
  `;
180
183
  }
181
184
 
185
+ toggleActionBarState(e) {
186
+ // toggle activity loader
187
+ this.disable = !this.disable;
188
+
189
+ // update action bar states is book is not available to browse or borrow.
190
+ if (e?.detail?.data?.error) {
191
+ const errorMsg = e.detail.data.error;
192
+ if (errorMsg.match(/not available to browse/gm)) {
193
+ const currStatus = {...this.lendingStatus, available_to_browse: false };
194
+ this.lendingStatus = currStatus;
195
+ } else if (errorMsg.match(/not available to borrow/gm)) {
196
+ const currStatus = { ...this.lendingStatus, available_to_borrow: false };
197
+ this.lendingStatus = currStatus;
198
+ }
199
+ }
200
+ }
201
+
182
202
  get iconClass() {
183
203
  return this.width <= mobileContainerWidth ? 'mobile' : 'desktop';
184
204
  }
@@ -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');