Issue

The existing web page content repeats the html for buttons which invoke popup modal dialogs. To add new popups the same html is copy-and-pasted resulting in very hard to maintain content.

Additionally, if the identifiers for the modal dialog is accidentally duplicated then the incorrect dialog would be brought up with no error feedback. This occurred quite often resulting a fair amount of time spent tracking down issues.

Proposal

To address this issue, the proposal was to componentize the popup and button to:

  1. Separate the user interfaces (UI) from the content. This allowed for common parts of the UI and interaction to be defined once in a component. This included any header and footer content, the main scrollable content area, and any common dialog buttons.

  2. Allow the UI to be built dynamically for different content. This is done as a utility script with each invocation handling a given web page.

Implementation

  1. One instance of the original UI for the button and modal dialog was extracted.

    • All hard-coded identifiers for button labels, button icons, dialog title, and DOM identifiers were replaced with tokens.
    • A $TOKEN_NAME$ syntax was used with TOKEN_NAME be replaced as needed to provide unique DOM identifiers.
    • The content itself was also replaced by unique token.
    • The html was saved as a reusable template file.
  2. A new utility was added to read the template file as well as parameters indicating where content resides, as well identifiers for button icon and label, dialog title, and DOM identifier. Basic string replacement per token is performed by the utility. The final content is returned by the utility.

  3. The original content was replaced by new DOM elements which identified where to insert a modal as well as the parameters for the modal. Using DOM elements is easy to understand and parse providing a quick way to add modals to any document. An arbitrary number of declarations can be made with one for each button/popup pair.

Reuse

Content for help is written in one or more Markdown files and then run through a utility to produce two HTML versions for each Markdown file.

  1. One is for a non-popup page and includes the insertion of the content into a templated HTML document which get's header and footer and additional wrapper information inserted in to.

  2. The other is the content without any additional wrappers.

The help pages used the first version and the utilities page popups use the second version. Popups can reference one or more of these content files as a "wrapper" file. It is this wrapper file that is used in the described implementation.

Thus all content is only written once and reused. Note that the Markdown content is suitable for sites which can directly display this content -- such in a Github repository.

Summary

The following figure provides a summary of the overall logic implemented. - Cylinders represent data which is either content or reusable template components - Boxes represent logic / processing which is performed.

Breakdown

Example

The follow shows the original content, the reusable template file created, and the separated content file and the reference which replaces the original content.

The button which triggers the pop-up is shown below as used on the help page. All corresponding help and video buttons shown follow the same usage pattern resulting in four references and four content files all using the sample template file.

Original Embedded Content
<!-- Video Button trigger modal -->
<div data-bs-toggle="tooltip" data-bs-title="Open Videos">
    <button type="button" class="btn  btn-sm btn-outline-primary"
        data-bs-toggle="modal" data-bs-target="#nodeEditorModal">
        <img src="../documents/icons/clapper.svg" width="16" height="16">
    </button>
</div>

<!-- Help Video Modal -->
<div class="modal fade" id="nodeEditorModal" tabindex="-1"

    aria-labelledby="nodeEditorModalLabel" aria-hidden="true">
    <div class="modal-dialog modal-dialog-scrollable modal-xl-custom">
        <div class="modal-content">
            <div class="modal-header">
                <h1 class="modal-title fs-5" id="nodeEditorModalLabel">Node Editor
                </h1>
                <button type="button" class="btn-close" data-bs-dismiss="modal"
                    aria-label="Close"></button>
            </div>

            <div class="modal-body p-2">
                <details open>
                    <summary>Creating Your First Material</summary>
                    <iframe src="https://www.youtube.com/embed/G2Z2U8AmArc?rel=0&vq=hd1080"
                        title="Creating Your First Material" width="100%"
                        height="600px" frameborder="0"
                        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
                        allowfullscreen>
                    </iframe>
                </details>
                <details open>
                    <summary>Using NodeGraphs</summary>
                    <iframe src="https://www.youtube.com/embed/ztDuVeyUwZU?rel=0&vq=hd1080"
                        title="Creating a Simple Shader" width="100%" height="600px"
                        frameborder="0"
                        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
                        allowfullscreen>
                    </iframe>
                </details>
                <details open>
                    <summary>Property Editor</summary>
                    <iframe src="https://www.youtube.com/embed/j_6vux9dPxU?rel=0&vq=hd1080"
                        title="Creating a Simple Shader" width="100%" height="600px"
                        frameborder="0"
                        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
                        allowfullscreen>
                    </iframe>
                </details>
            </div>

            <div class="modal-footer">
                <button type="button" class="btn btn-primary"
                    data-bs-dismiss="modal">Close</button>
            </div>
        </div>
    </div>
</div>
Template File

Note that tokens such as $HELP_MODAL$ are reused for different places to provide: comments and unique identifiers for the button, the modal and modal label.

<!-- $HELP_MODAL$ Modal Button -->
<div data-bs-toggle="tooltip" data-bs-title="$HELP_BUTTON_TITLE$">
    <button type="button" class="btn  btn-sm btn-outline-primary"
        data-bs-toggle="modal" data-bs-target="#$HELP_MODAL$">
        <img src="$HELP_BUTTON_ICON$" width="16" height="16">
    </button>
</div>

<!-- $HELP_MODAL$ Modal -->
<div class="modal fade" id="$HELP_MODAL$" tabindex="-1"

    aria-labelledby="$HELP_MODAL$_Label" aria-hidden="true">
    <div class="modal-dialog modal-dialog-scrollable modal-xl-custom">
        <div class="modal-content">
            <div class="modal-header">
                <h1 class="modal-title fs-5" id="$HELP_MODAL$_Label">$HELP_CONTENT_TITLE$
                </h1>
                <button type="button" class="btn-close" data-bs-dismiss="modal"
                    aria-label="Close"></button>
            </div>

            <div class="modal-body p-2">
                $HELP_MODAL_CONTENT$
            </div>

            <div class="modal-footer">
                <button type="button" class="btn btn-primary"
                    data-bs-dismiss="modal">Close</button>
            </div>
        </div>
    </div>
</div>
Content File
<details open>
    <summary>Creating Your First Material</summary>
    <iframe src="https://www.youtube.com/embed/G2Z2U8AmArc?rel=0&vq=hd1080"
        title="Creating Your First Material" width="100%"
        height="600px" frameborder="0"
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
        allowfullscreen>
    </iframe>
</details>
<details open>
    <summary>Using NodeGraphs</summary>
    <iframe src="https://www.youtube.com/embed/ztDuVeyUwZU?rel=0&vq=hd1080"
        title="Creating a Simple Shader" width="100%" height="600px"
        frameborder="0"
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
        allowfullscreen>
    </iframe>
</details>
<details open>
    <summary>Property Editor</summary>
    <iframe src="https://www.youtube.com/embed/j_6vux9dPxU?rel=0&vq=hd1080"
        title="Creating a Simple Shader" width="100%" height="600px"
        frameborder="0"
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
        allowfullscreen>
    </iframe>
</details>
Declaration Reference
  <div class="help_modal" content_file="node_editor_videos_content.html" ,
        title="Node Editor Videos" , button_title="Open Videos" id="node_editor_videos_popup"
        button_icon="../documents/icons/clapper.svg"></div>