[SalesForce] Prevent LWC Modal from Closing when Clicking Anywhere Outside it

There is a stackexchange post here that is trying to make it possible for clicking outside the modal to allow the modal to close

However, I am trying to do the opposite. I have a parent component that passes data to a child component from a wire method. But whenever I click outside the modal, the modal closes and I can see a value of undefined in the console. I'm not sure where this value is coming from since I removed all console.logs from my code to see if it was being generated somewhere else. Is there a property or a value in the LWC design system that you must include/exclude in order prevent this behavior?

I pulled this code from the following article; this is much cleaner than my implementation and the code he is using I just tested and it is still closing the modal when they click outside the modal.

HTML – Child Confirmation

<template>
    <lightning-card if:true={visible}>
        <div class="slds-container_small">
            <section role="dialog" tabindex="-1" aria-labelledby="modal-heading-01" aria-modal="true" aria-describedby="modal-content-id-1" class="slds-modal slds-fade-in-open">
                <div class="slds-modal__container">
                    <header class="slds-modal__header">
                        <h2 id="modal-heading-01" class="slds-text-heading_medium slds-hyphenate">{title}</h2>
                    </header>
                    <div class="slds-modal__content slds-p-around_medium" id="modal-content-id-1">
                        <p>{message}</p>
                    </div>
                    <footer class="slds-modal__footer">
                        <lightning-button variant="neutral"
                                          name="cancel"
                                          label={cancelLabel}
                                          title={cancelLabel}
                                          onclick={handleClick} ></lightning-button>
                        <lightning-button variant="brand"
                                          name="confirm"
                                          label={confirmLabel}
                                          title={confirmLabel}
                                          onclick={handleClick} ></lightning-button>
                    </footer>
                </div>
            </section>
            <div class="slds-backdrop slds-backdrop_open"></div>
        </div>
    </lightning-card>
</template>

JS Child Confirmation

import {LightningElement, api} from 'lwc';

export default class ConfirmationDialog extends LightningElement {
    @api visible; //used to hide/show dialog
    @api title; //modal title
    @api name; //reference name of the component
    @api message; //modal message
    @api confirmLabel; //confirm button label
    @api cancelLabel; //cancel button label
    @api originalMessage; //any event/message/detail to be published back to the parent component

    //handles button clicks
    handleClick(event){
        //creates object which will be published to the parent component
        let finalEvent = {
            originalMessage: this.originalMessage,
            status: event.target.name
        };

        //dispatch a 'click' event so the parent component can handle it
        this.dispatchEvent(new CustomEvent('click', {detail: finalEvent}));
    }
}

HTML Parent

<!-- Parent Component -->
<template>
    <lightning-card title="Testing Confirmation Dialog" icon-name="custom:custom60">
        <lightning-button label="Open Confirmation" slot="actions" onclick={handleClick} name="openConfirmation"></lightning-button>
        <div class="slds-text-body_regular slds-p-around--small">{displayMessage}</div>
    </lightning-card>
    <c-confirmation-dialog title='Confirmation Title'
                           message='Do you want to proceed?'
                           confirm-label='Yes'
                           cancel-label='No'
                           visible={isDialogVisible}
                           original-message={originalMessage}
                           name="confirmModal"
                           onclick={handleClick}>
    </c-confirmation-dialog>
</template>

JS – Parent Component

import {LightningElement, track} from 'lwc';

export default class ParentComponent extends LightningElement {
    @track isDialogVisible = false;
    @track originalMessage;
    @track displayMessage = 'Click on the \'Open Confirmation\' button to test the dialog.';

    handleClick(event){
        if(event.target.name === 'openConfirmation'){
            //it can be set dynamically based on your logic
            this.originalMessage = 'test message';
            //shows the component
            this.isDialogVisible = true;
        }else if(event.target.name === 'confirmModal'){

            //when user clicks outside of the dialog area, the event is dispatched with detail value  as 1
            if(event.detail !== 1){
                //gets the detail message published by the child component
                this.displayMessage = 'Status: ' + event.detail.status + '. Event detail: ' + JSON.stringify(event.detail.originalMessage) + '.';

                //you can do some custom logic here based on your scenario
                if(event.detail.status === 'confirm') {
                    //do something
                }else if(event.detail.status === 'cancel'){
                    //do something else
                }
            }

            //hides the component
            this.isDialogVisible = false;
        }
    }
}

Any help would be greatly appreciated. Thanks!

Best Answer

The problem with your code you have defined a custom event with name click in your child component. Basically you should not use it for your custom event. It is overriding the standard onclick behavior.

//dispatch a 'click' event so the parent component can handle it
this.dispatchEvent(new CustomEvent('click', {detail: finalEvent}));

Just change the custom event name to something else:-

//dispatch a 'click' event so the parent component can handle it
 this.dispatchEvent(new CustomEvent('closemodalclick', {detail: finalEvent}));

and handle it in parent as:-

<c-child title='Confirmation Title'
                           message='Do you want to proceed?'
                           confirm-label='Yes'
                           cancel-label='No'
                           visible={isDialogVisible}
                           original-message={originalMessage}
                           name="confirmModal"
                           onclosemodalclick={handleClick}>

check the working code your here:- Playground