Howell Manongsong Full Stack Developer based in Canada. NetSuite Certified SuiteCloud Developer. Creator of SuiteApp.com, and key contributor to the development of NetSuite's SuiteApp Marketplace. A philomath, driven by an insatiable curiosity and a relentless passion for learning.

Revolutionize Your NetSuite Experience with the Ultimate Split Pane Utility!

5 min read

This article presents a free, versatile NetSuite split pane utility that will supercharge your (users’) NetSuite experience. It also includes sample code snippets to get you going. NetSuite Insights would like to thank the author for generously providing this capacity to the community at no cost.


Background

While working on a custom approval Suitelet, I encountered a challenge related to the efficiency of the approval process using this approach. The Suitelet contained a list of transactions along with their respective details for the approver to review. However, despite having most of the necessary information within the Suitelet, the existing workflow still proved to be cumbersome. Opening each transaction in a new tab to verify additional details and then switching back to the Suitelet was not only time-consuming but also disrupted the intended seamless flow of the approval process.

Without split pane, reviewing transactions for approval is very cumbersome.
Without split pane, reviewing transactions for approvals is cumbersome and very inefficient

Recognizing the need for a more streamlined approach, I was motivated to come up with a solution that would enhance the user experience for the approver. My goal was to develop a library that would allow transactions to be easily viewed and assessed on the same page, eliminating the need for constant tab-switching. I succeeded!

Split pane can be used in a Suitelet to review transactions before approval.
Incorporating split pane make the approval process intuitive, more efficient, and more interesting

As I focused on improving the approval process, I realized that the library had potential beyond just approvals. I found that its features could be applied to other scenarios as well, such as loading relevant links in the pane view on page load. This realization motivated me to make the library more versatile, catering to a wider range of business needs.


Example Use Cases

There are several scenarios in which the split pane utility can add value. Here are my favorite ones.

Use Case 1: Loading a preview of a transaction in PDF format on page load

Users may want to immediately see the PDF format of the current transaction in edit/view mode. To close the split pane, they can easily drag it to the leftmost side of the page or double-click on the bar/divider.

/**
 * @NApiVersion 2.1
 * @NScriptType UserEventScript
 */
define(['N/runtime'], (runtime) => {
  /**
   * Function definition to be triggered before the record is loaded.
   *
   * @param {Object} context
   */
  const beforeLoad = (context) => {
    try {
      instantiateSplitPane(context);
    } catch (err) {
      log.error({
        title: err.name,
        details: err.message,
      });
    }
  };

  /**
   * Instantiates the Split Pane view through a User Event Script
   *
   * @param {Object} context
   */
  const instantiateSplitPane = (context) => {
    if (runtime.executionContext !== runtime.ContextType.USER_INTERFACE) {
      return;
    }

    const allowedTypes = [context.UserEventType.EDIT, context.UserEventType.VIEW];

    if (context.type.includes(allowedTypes)) {
      return;
    }

    const recordId = context.newRecord.id;
    const pageLink = `/app/accounting/print/hotprint.nl?regular=T&sethotprinter=T&formnumber=92&trantype=custinvc&&label=Invoice&printtype=transaction&id=${recordId}`;

    const splitPaneHTML = context.form.addField({
      id: 'custpage_hex_splitpane',
      label: 'Split Pane',
      type: 'inlinehtml',
    });
    splitPaneHTML.defaultValue = `
      <script>
        require(['SuiteScripts/HEX_LIB_SplitPane.js'], (splitPane) => {
          splitPane.openSplitPane('${pageLink}', 30);
        });
      </script>`;
  };
  return {
    beforeLoad,
  };
});
Split pane automatically load when a transaction is viewed.



Use Case 2: Loading a record’s attachment in the split pane on button click

In certain cases, users may prefer the option to open the split pane view only when necessary. To accommodate this scenario, a button can be added through a user event script to open the split pane.

Split pane can be used to load attachments or other files on button click.

Here’s a sample code for the use case illustrated above.

/**
 * @NApiVersion 2.1
 * @NScriptType UserEventScript
 */

define(['N/runtime'], (runtime) => {
  /**
   * Function definition to be triggered before record is loaded.
   *
   * @param {Object} context
   */
  const beforeLoad = (context) => {
    try {
      context.form.addButton({
        id: 'custpage_hex_splitpane',
        label: 'Open Most Recent Attachment',
        functionName: 'openMostRecentAttachment',
      });

      context.form.clientScriptModulePath = 'SuiteScripts/HEX_CS_Invoice.js';
    } catch (err) {
      log.error({
        title: err.name,
        details: e.message,
      });
    }
  };

  return {
    beforeLoad,
  };
});

Here’s the client script referenced in the user event script above:

/**
 * @NApiVersion 2.1
 * @NScriptType ClientScript
 */

define(['N/currentRecord', 'N/search', 'SuiteScripts/HEX_LIB_SplitPane.js'], (
  currentRecord,
  search,
  splitPane
) => {
  /**
   * Function to be executed after page is initialized.
   *
   * @param {Object} context
   */
  const openMostRecentAttachment = () => {
    const currentRec = currentRecord.get();
    const fileURL = getMostRecentAttachmentURL(currentRec.id);

    try {
      splitPane.openSplitPane(fileURL);
    } catch (err) {
      console.log(err);
    }
  };

  /**
   * Retrieves the most recent attachment of the current invoice record
   *
   * @param {Object} context
   */
  const getMostRecentAttachmentURL = (invoiceId) => {
    const fileSearch = search
      .create({
        type: search.Type.INVOICE,
        filters: [
          search.createFilter({
            name: 'internalid',
            operator: search.Operator.ANYOF,
            values: [invoiceId],
          }),
          search.createFilter({
            name: 'mainline',
            operator: search.Operator.IS,
            values: ['T'],
          }),
        ],
        columns: [
          search.createColumn({name: 'url', join: 'file'}),
          search.createColumn({
            name: 'created',
            join: 'file',
            sort: search.Sort.DESC,
          }),
        ],
      })
      .run()
      .getRange({start: 0, end: 1});

    const fileURL = fileSearch[0]?.getValue({name: 'url', join: 'file'});

    return fileURL;
  };

  return {
    pageInit: () => {},
    openMostRecentAttachment,
  };
});

When creating Suitelets containing a list of records, it is a common practice to include a column containing a link to the respective record for users to view. This use case is exhibited in the background section of this article.

To enhance the user experience, we can allow users to conveniently access and view these records within the same page through the split pane.

searchObj.run().each((result) => {
  const docNumber = result.getValue({ name: 'tranid' });
  const url = `/app/accounting/transactions/transaction.nl?id=${result.id}&ifrmcntnr=T`;

  sublist.setSublistValue({
    id: 'custpage_document_number',
    line: i,
    value: splitPane.generateLink(docNumber, url), // Defaults to 50% width
  });

  ...
});

Once the generateLink function has been invoked, it will create a href element that contains an inline onclick event handler. This event handler is responsible for opening the link within the split pane view.  In the event of an unexpected error within the library, the generated link will revert to the default behavior of an href tag.

It is important to consider that the TEXT field type has a maximum character limit of 300. In certain cases, the generated link may exceed this limit due to factors such as the length of the label name and the URL, and the inline event handler embedded within the link. To prevent exceeding the limit, I recommend using the TEXTAREA field type instead.

Considerations

Document Object Model (DOM) Manipulation

The Split Pane library operates on the client side, utilizing DOM manipulation to build the split pane. Although this approach can be seen as a “hack”, as NetSuite discourages direct DOM manipulation due to potential breaking changes caused by internal product updates, we can mitigate the associated risks by implementing precautionary measures such as enclosing function calls in a try-catch block.

Theme Inheritance

For a better user experience (UX), the library was designed to adopt the color theme of the user as specified in the User Preferences.

Split pane inherits the current theme with a reasonable fallback if it cannot be retrieved.

To implement this, I initially considered using the N/config module to fetch the user’s color theme preference and apply it to the elements of the split pane. However, since the library operates on the client side and the module is only accessible on server side, this approach was not feasible. As an alternative solution, I came up with the idea of retrieving the color of the navigation bar, which is present on most, if not all, NetSuite pages, using the following code snippet.

const navbar = document.getElementsByClassName('n-w-header__navigation')[0];

const barColor = navbar ? window.getComputedStyle(navbar).backgroundColor : '#444444';

It is important to mention that if the element does not exist for various reasons, such as NetSuite updating the element selector, the library will default to using the color #444444. The provided code snippet is flexible and useful when you want certain elements to match the user’s current theme.


Conclusion

Despite the library’s seemingly simplistic nature, its potential to significantly reduce the time and effort invested by users is remarkable. As these time savings accumulate, they can result in exponential gains in productivity. Its simplicity belies its true power, making it an invaluable tool for developers and NetSuite users seeking enhanced efficiency and productivity.


How to Get the Split Pane Utility

I am happy to share this utility for free under the MIT license with anyone who needs it. Head over to my Github project to download the single file source code. Feel free to use it in your personal and commercial products per the license conditions. As a courtesy to the community, be sure to share any useful enhancements with the community, for example via merge requests in Github.


NetSuite Insights is on a mission to raise the standards around NetSuite practices, one insight at a time. If that resonates with you, learn how you can become an author/collaborator here.

Don’t miss a beat – subscribe to our no-nonsense email list to have these game-changing insights hit your inbox as soon as they’re published. Ignorance? Nah, you’ve got a smarter option. Choose wisdom, choose insights!

Related Posts

Howell Manongsong Full Stack Developer based in Canada. NetSuite Certified SuiteCloud Developer. Creator of SuiteApp.com, and key contributor to the development of NetSuite's SuiteApp Marketplace. A philomath, driven by an insatiable curiosity and a relentless passion for learning.

8 Replies to “Revolutionize Your NetSuite Experience with the Ultimate Split Pane Utility!”

  1. You haven’t created a User Event script that makes use of the library only the library file.
    You need to re-read and understand how this works the library on it’s own in the file cabinet does nothing.

  2. I tried, these steps yet didn’t work for me:
    Download the HEX_LIB_SplitPane.js file from the GitHub repository.
    Upload the file in the NetSuite File Cabinet.
    Update the PANE_LIB_PATH section in the file to match the correct file directory where the library is located. By default, the location is set to the root directory of the SuiteScripts folder.

    Edit Invoice->tried moving the screen with holding mouse but nothing, kindly advice what I am doing wrong.

    1. Yes! I was excited to stumble upon this utility. I don’t think we can get this to work for saved searches as we need a way to “hook into” the page and those native lists don’t offer entry points. That said, I’ll ask Howell if he has any ideas.

    2. Thank you, Marty!
      Unfortunately, we cannot trigger the split pane within the saved search results view. On a positive note, I’m actively working on the development of a Chrome Extension that aims to enable the split pane functionality for a majority, if not all, of the NetSuite links or buttons.

Leave a Reply

Your email address will not be published. Required fields are marked *

×