Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ui5-barcode-scanner-dialog): added support for custom header and footer slots #10066

Merged
merged 3 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 124 additions & 4 deletions packages/fiori/cypress/specs/BarcodeScannerDialog.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import "../../src/BarcodeScannerDialog.js";
import type BarcodeScannerDialog from "../../src/BarcodeScannerDialog.js";

describe("BarcodeScannerDialog", () => {
let openDialogHandler: EventListener | null;
let handleScanSuccess: (event: CustomEvent) => void;
let handleScanError: (event: CustomEvent) => void;

Expand Down Expand Up @@ -49,6 +50,12 @@ describe("BarcodeScannerDialog", () => {
handleScanError = undefined!;
}

const btnScan = doc.querySelector("#btnScan");
if (btnScan && openDialogHandler) {
btnScan.removeEventListener("click", openDialogHandler);
}
openDialogHandler = null;

// Clear the scan result and error
const scanResultElement = doc.querySelector("#scanResult")!;
const scanErrorElement = doc.querySelector("#scanError")!;
Expand Down Expand Up @@ -82,7 +89,7 @@ describe("BarcodeScannerDialog", () => {
// Close the dialog using the close button
cy.get("@dialog")
.shadow()
.find("ui5-dialog [slot=footer] ui5-button")
.find("ui5-dialog slot[name=footer] ui5-button")
.realClick();

// Verify the dialog is closed
Expand Down Expand Up @@ -188,7 +195,7 @@ describe("BarcodeScannerDialog", () => {
// Close the dialog using the close button
cy.get("@dialog")
.shadow()
.find("ui5-dialog [slot=footer] ui5-button")
.find("ui5-dialog slot[name=footer] ui5-button")
.realClick();

// Verify the dialog is closed
Expand Down Expand Up @@ -266,7 +273,7 @@ describe("BarcodeScannerDialog", () => {
const dlgScan = $dialog.get(0) as BarcodeScannerDialog;

// Simulate the scan success
dlgScan.fireEvent("scan-success", {
dlgScan.fireDecoratorEvent("scan-success", {
text: "mocked-scan-result",
rawBytes: new Uint8Array(),
});
Expand Down Expand Up @@ -301,7 +308,7 @@ describe("BarcodeScannerDialog", () => {
const dlgScan = $dialog.get(0) as BarcodeScannerDialog;

// Simulate the scan error
dlgScan.fireEvent("scan-error", {
dlgScan.fireDecoratorEvent("scan-error", {
message: "mocked-scan-error",
});
});
Expand Down Expand Up @@ -347,3 +354,116 @@ describe("BarcodeScannerDialog", () => {
});
});
});

describe("BarcodeScannerDialog with Custom Slots", () => {
let openDialogHandler: EventListener | null;
let closeDialogHandler: EventListener | null;

beforeEach(() => {
cy.mount(html`
<ui5-barcode-scanner-dialog id="dlgScanCustom">
<div slot="header" class="custom-dialog-header">
<ui5-title level="H2">My Custom Header</ui5-title>
</div>
<div slot="footer" class="custom-dialog-footer">
<ui5-button id="customCloseBtn">My Custom Close Button</ui5-button>
</div>
</ui5-barcode-scanner-dialog>
<ui5-button id="btnScanCustom" icon="camera">Open Custom Scanner Dialog</ui5-button>
`);

cy.get("#dlgScanCustom").as("customDialog");
cy.get("#btnScanCustom").as("customButton");
cy.get("@customDialog")
.shadow()
.find(".ui5-barcode-scanner-dialog-video")
.as("videoElement");

cy.document().then(doc => {
const dlgScanCustom = doc.querySelector<BarcodeScannerDialog>("#dlgScanCustom")!;
const btnScanCustom = doc.querySelector("#btnScanCustom")!;
const btnScanCustomClose = doc.querySelector("#customCloseBtn")!;

btnScanCustom.addEventListener("click", () => {
dlgScanCustom.open = true;
});

btnScanCustomClose.addEventListener("click", () => {
dlgScanCustom.open = false;
});
});
});

afterEach(() => {
cy.document().then(doc => {
const btnScanCustom = doc.querySelector("#btnScanCustom");
const btnScanCustomClose = doc.querySelector("#customCloseBtn");

if (btnScanCustom && openDialogHandler) {
btnScanCustom.removeEventListener("click", openDialogHandler);
}
openDialogHandler = null;

if (btnScanCustomClose && closeDialogHandler) {
btnScanCustomClose.removeEventListener("click", closeDialogHandler);
}

closeDialogHandler = null;
});
});

it("renders custom header when provided", () => {
// Click the button to open the custom dialog
cy.get("@customButton").realClick();

// Wait for the video to be ready
cy.get("@videoElement").should($video => {
const videoEl = $video[0] as HTMLVideoElement;
expect(videoEl.readyState, "Video readyState should be >= 1").to.be.at.least(1);
});

// Assert that the dialog is open
cy.get("@customDialog")
.shadow()
.find("ui5-dialog")
.should("have.attr", "open");

// Verify that the custom header is rendered
cy.get("@customDialog")
.find("[slot=header] ui5-title")
.should("contain.text", "My Custom Header");
});

it("renders custom footer and functions correctly", () => {
// Click the button to open the custom dialog
cy.get("@customButton").realClick();

// Wait for the video to be ready
cy.get("@videoElement").should($video => {
const videoEl = $video[0] as HTMLVideoElement;
expect(videoEl.readyState, "Video readyState should be >= 1").to.be.at.least(1);
});

// Assert that the dialog is open
cy.get("@customDialog")
.shadow()
.find("ui5-dialog")
.should("have.attr", "open");

// Verify that the custom footer is rendered
cy.get("@customDialog")
.find("[slot=footer] ui5-button")
.should("contain.text", "My Custom Close Button");

// Test that the custom button closes the dialog
cy.get("@customDialog")
.find("#customCloseBtn")
.realClick();

// Verify the dialog is closed
cy.get("@customDialog")
.shadow()
.find("ui5-dialog")
.should("not.have.attr", "open");
});
});
5 changes: 3 additions & 2 deletions packages/fiori/src/BarcodeScannerDialog.hbs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<ui5-dialog stretch class="ui5-barcode-scanner-dialog-root" .open="{{_open}}" @ui5-close="{{_closeDialog}}">
<slot name="header"></slot>
<div class="ui5-barcode-scanner-dialog-video-wrapper">
<video autoplay playsinline muted class="ui5-barcode-scanner-dialog-video"></video>
<canvas class="ui5-barcode-scanner-dialog-overlay"></canvas>
</div>
<div slot="footer" class="ui5-barcode-scanner-dialog-footer">
<slot name="footer" class="ui5-barcode-scanner-dialog-footer">
<ui5-button design="Transparent" @click={{_closeDialog}}>{{_cancelButtonText}}</ui5-button>
</div>
</slot>
</ui5-dialog>
<ui5-busy-indicator class="ui5-barcode-scanner-dialog-busy" ?active={{loading}} size="L"
text="{{_busyIndicatorText}}"></ui5-busy-indicator>
29 changes: 29 additions & 0 deletions packages/fiori/src/BarcodeScannerDialog.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import slot from "@ui5/webcomponents-base/dist/decorators/slot.js";
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
import Dialog from "@ui5/webcomponents/dist/Dialog.js";
Expand Down Expand Up @@ -131,6 +132,34 @@ type BarcodeScannerDialogScanErrorEventDetail = {
})

class BarcodeScannerDialog extends UI5Element {
/**
* Defines the header HTML Element.
*
* **Note:** If `header` slot is provided, the labelling of the dialog is a responsibility of the application developer.
* `accessibleName` should be used.
*
* @public
* @since 2.4.0
*/
@slot()
header!: Array<HTMLElement>;

/**
* Defines the footer HTML Element.
*
* **Note:** When you provide custom content for the `footer` slot, the default close button is not rendered.
* This means you need to include your own mechanism within the custom `footer` to close the dialog,
* such as a button with an event listener that closes the dialog.
*
* **Note:** If the `footer` slot is not provided, a default footer with a close button is rendered automatically,
* allowing users to close the dialog without any additional implementation.
*
* @public
* @since 2.4.0
*/
@slot()
footer!: Array<HTMLElement>;

/**
* Indicates whether the dialog is open.
*
Expand Down
4 changes: 4 additions & 0 deletions packages/fiori/src/themes/BarcodeScannerDialog.css
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
.ui5-barcode-scanner-dialog-root::part(content) {
display: flex;
flex-direction: column;
padding: .4375rem;
}

.ui5-barcode-scanner-dialog-video-wrapper {
position: relative;
min-height: 0;
}

/* video */
Expand All @@ -29,6 +32,7 @@
display: flex;
justify-content: flex-end;
width: 100%;
padding-top: .4375rem;
}

/* busy indicator */
Expand Down
39 changes: 38 additions & 1 deletion packages/fiori/test/pages/BarcodeScannerDialog.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,18 @@

<ui5-barcode-scanner-dialog id="dlgScan"></ui5-barcode-scanner-dialog>

<ui5-button id="btnScan" icon="camera" tooltip="Start Camera">Scan</ui5-button>
<ui5-barcode-scanner-dialog id="dlgScanCustom">
<div slot="header" class="custom-dialog-header">
<ui5-title level="H2">My Custom Header</ui5-title>
</div>
<div slot="footer" class="custom-dialog-footer">
<ui5-button id="btnCloseDialogCustom" icon="da" design="Attention">My Custom Close Button</ui5-button>
</div>
</ui5-barcode-scanner-dialog>

<ui5-button id="btnScan" icon="camera" tooltip="Start Camera">Open Default Dialog</ui5-button>
<ui5-button id="btnScanCustom" icon="camera" tooltip="Start Camera Custom" design="Emphasized">Open Custom Dialog</ui5-button>

<div>
<ui5-label id="scanResult"></ui5-label>
<ui5-label id="scanError"></ui5-label>
Expand All @@ -34,7 +45,10 @@

<script>
const dlgScan = document.getElementById("dlgScan");
const dlgScanCustom = document.getElementById("dlgScanCustom");
const btnScan = document.getElementById("btnScan");
const btnScanCustom = document.getElementById("btnScanCustom");
const btnCloseDialogCustom = document.getElementById("btnCloseDialogCustom");
const scanResult = document.getElementById("scanResult");
const scanError = document.getElementById("scanError");

Expand All @@ -43,19 +57,42 @@
document.querySelector(".open-state").textContent = `${dlgScan.open}`;
});

btnScanCustom.addEventListener("click", (event) => {
dlgScanCustom.open = true;
document.querySelector(".open-state").textContent = `${dlgScanCustom.open}`;
});

btnCloseDialogCustom.addEventListener("click", (event) => {
dlgScanCustom.open = false;
});

dlgScan.addEventListener("ui5-scan-success", (event) => {
scanResult.innerHTML = event.detail.text;
dlgScan.open = false;
});

dlgScanCustom.addEventListener("ui5-scan-success", (event) => {
scanResult.innerHTML = event.detail.text;
dlgScanCustom.open = false;
});

dlgScan.addEventListener("ui5-scan-error", (event) => {
scanError.innerHTML = event.detail.message;
dlgScan.open = false;
});

dlgScanCustom.addEventListener("ui5-scan-error", (event) => {
scanError.innerHTML = event.detail.message;
dlgScanCustom.open = false;
});

dlgScan.addEventListener("ui5-close", (event) => {
document.querySelector(".open-state").textContent = `${dlgScan.open}`;
});

dlgScanCustom.addEventListener("ui5-close", (event) => {
document.querySelector(".open-state").textContent = `${dlgScanCustom.open}`;
});
</script>
</body>
</html>
11 changes: 10 additions & 1 deletion packages/fiori/test/pages/styles/BarcodeScannerDialog.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
.barcodescannerdialog1auto {
background-color: var(--sapBackgroundColor);
background-color: var(--sapBackgroundColor);
}

.custom-dialog-header {
text-align: center;
padding-bottom: .4375rem;
}

.custom-dialog-footer {
height: auto;
}