diff --git a/src/apps/order-material/order-material.dev.jsx b/src/apps/order-material/order-material.dev.jsx index eac843cc..4c1bd00f 100644 --- a/src/apps/order-material/order-material.dev.jsx +++ b/src/apps/order-material/order-material.dev.jsx @@ -24,7 +24,16 @@ export function Entry() { "Success message", "Materialet er bestilt, dit bibliotek vil give besked når det er klar til afhentning." )} - id={text("Material ID", "870970-basis:54172613")} + // Prefer text instead of array knob here to simulate a naive mount using + // data attributes (text) as props. + ids={text( + "Material IDs (separate by ,)", + [ + "870970-basis:47092183", + "870970-basis:51980190", + "870970-basis:23154382" + ].join(",") + )} loginUrl={text( "Login URL", "https://lollandbib.dk/adgangsplatformen/login?destination=ting/object/:id" diff --git a/src/apps/order-material/order-material.entry.jsx b/src/apps/order-material/order-material.entry.jsx index abdcc39a..86a214e8 100644 --- a/src/apps/order-material/order-material.entry.jsx +++ b/src/apps/order-material/order-material.entry.jsx @@ -7,6 +7,19 @@ import OpenPlatform from "../../core/OpenPlatform"; const client = new OpenPlatform(); +/** + * Transform a set of ids to an array of ids. + * + * This supports transforming a single string with multiple ids separated by , + * (used in naive app mounts using data attributes as props) to an array of ids. + * + * @param {string|string[]} One or more ids. + * @returns {string[]} Array of ids. + */ +function idsArray(ids) { + return typeof ids === "string" ? ids.split(",") : ids; +} + function OrderMaterialEntry({ text, successText, @@ -16,7 +29,7 @@ function OrderMaterialEntry({ progressText, unavailableText, invalidPickupBranchText, - id, + ids, loginUrl, pickupBranch, expires @@ -26,7 +39,7 @@ function OrderMaterialEntry({ function orderMaterial() { setStatus("processing"); client - .orderMaterial({ pids: [id], pickupBranch, expires }) + .orderMaterial({ pids: idsArray(ids), pickupBranch, expires }) .then(function materialOrdered() { setStatus("finished"); }) @@ -47,7 +60,7 @@ function OrderMaterialEntry({ } else { // Check that the material is available for ILL. client - .canBeOrdered(id) + .canBeOrdered(idsArray(ids)) .then(function onAvailabilityResult(available) { setStatus(available ? "ready" : "unavailable"); }) @@ -60,7 +73,7 @@ function OrderMaterialEntry({ setStatus("failed"); }); }, - [id, pickupBranch] + [ids, pickupBranch] ); return ( @@ -76,7 +89,7 @@ function OrderMaterialEntry({ status={status} onClick={orderMaterial} loginUrl={loginUrl} - materialId={id} + materialIds={idsArray(ids)} /> ); } @@ -90,7 +103,10 @@ OrderMaterialEntry.propTypes = { invalidPickupBranchText: PropTypes.string, successText: PropTypes.string, successMessage: PropTypes.string, - id: PropTypes.string.isRequired, + ids: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.arrayOf(PropTypes.string) + ]).isRequired, loginUrl: urlPropType.isRequired, pickupBranch: PropTypes.string.isRequired, expires: PropTypes.string.isRequired diff --git a/src/apps/order-material/order-material.jsx b/src/apps/order-material/order-material.jsx index 9d3d999d..b2a929c4 100644 --- a/src/apps/order-material/order-material.jsx +++ b/src/apps/order-material/order-material.jsx @@ -20,7 +20,7 @@ function OrderMaterial({ successText, successMessage, loginUrl, - materialId + materialIds }) { const [open, setOpen] = useState(true); const closeDialog = () => setOpen(false); @@ -79,7 +79,9 @@ function OrderMaterial({ ? replacePlaceholders({ text: loginUrl, tags: { - id: encodeURIComponent(materialId) + // Urls only support a single material id so assume we + // want to use the first. + id: encodeURIComponent(materialIds.first) } }) : undefined @@ -116,7 +118,7 @@ OrderMaterial.propTypes = { "failed", "finished" ]), - materialId: PropTypes.string.isRequired + materialIds: PropTypes.arrayOf(PropTypes.string).isRequired }; OrderMaterial.defaultProps = { diff --git a/src/apps/order-material/order-material.test.js b/src/apps/order-material/order-material.test.js index 9563aef5..756e58cf 100644 --- a/src/apps/order-material/order-material.test.js +++ b/src/apps/order-material/order-material.test.js @@ -17,32 +17,59 @@ describe("Order material", () => { cy.contains("Dit afhentningsbibliotek modtager ikke fjernlån"); }); - it("Should display message if material can't be ordered", () => { - cy.server(); - cy.route({ - method: "GET", - url: "https://openplatform.dbc.dk/v3/libraries*", - status: 200, - response: { - statusCode: 200, - data: [{ willReceiveIll: "1" }] - } - }); + [ + { + title: "Should display message if a single material can't be ordered", + availability: [{ orderPossible: false }], + orderable: false + }, + { + title: "Should display message if a set of materials can't be ordered", + availability: [{ orderPossible: false }, { orderPossible: false }], + orderable: false + }, + { + title: "Should display button if a single material can be ordered", + availability: [{ orderPossible: true }], + orderable: true + }, + { + title: "Should display button if a set of materials can be ordered", + availability: [{ orderPossible: true }, { orderPossible: false }], + orderable: true + } + ].forEach(scenario => { + it(scenario.title, () => { + cy.server(); + cy.route({ + method: "GET", + url: "https://openplatform.dbc.dk/v3/libraries*", + status: 200, + response: { + statusCode: 200, + data: [{ willReceiveIll: "1" }] + } + }); - cy.route({ - method: "GET", - url: "https://openplatform.dbc.dk/v3/availability*", - status: 200, - response: { - statusCode: 200, - data: [{ orderPossible: false }] - } - }); + cy.route({ + method: "GET", + url: "https://openplatform.dbc.dk/v3/availability*", + status: 200, + response: { + statusCode: 200, + data: scenario.availability + } + }); - cy.visit("/iframe.html?id=apps-order-material--entry"); + cy.visit("/iframe.html?id=apps-order-material--entry"); - cy.contains("Bestil materiale").should("not.exist"); - cy.contains("Kan ikke fjernlånes"); + if (scenario.orderable) { + cy.contains("Bestil materiale"); + } else { + cy.contains("Bestil materiale").should("not.exist"); + cy.contains("Kan ikke fjernlånes"); + } + }); }); it("Should send order request to OpenPlatform when clicked", () => { @@ -84,7 +111,7 @@ describe("Order material", () => { status: 200, response: { statusCode: 200, - data: [{ orderPossible: true }] + data: [{ orderPossible: true }, { orderPossible: false }] } }); diff --git a/src/core/OpenPlatform.js b/src/core/OpenPlatform.js index 8c5decbe..a8fb70ea 100644 --- a/src/core/OpenPlatform.js +++ b/src/core/OpenPlatform.js @@ -139,20 +139,19 @@ class OpenPlatform { } /** - * Check if a material can be ordered. + * Check if at least one material in a set of materials can be ordered. * - * @param {string} pid - id of work + * The materials represented by the ids are supposed to be equivalent and thus + * we only care if at least one of the materials can be ordered. + * + * @param {string[]} pids - ids of work(s) to check. * @returns {Promise} */ - async canBeOrdered(pid) { - return this.getAvailability({ pids: [pid] }).then(function getResult( - response - ) { - // The API says there can be more than one reply, but nobody - // defines what that means. So if case there's more than one - // reply, only return true if all items are order-able. - - return response.every(function orderIsPossible(orderStat) { + async canBeOrdered(pids) { + return this.getAvailability({ pids }).then(function getResult(response) { + // The API returns availability information for each pid. Reduce these to + // a single value by checking if at least one material can be ordered. + return response.some(function orderIsPossible(orderStat) { return orderStat.orderPossible; }); });