View our other brands
// Configuration object for centralized settings
const TabbedNavConfig = {
  DESKTOP_BREAKPOINT: 1201,
  DEBOUNCE_DELAY: 250,
  OPEN_FIRST_TAB: true,
  CLOSE_TAB_ON_MOUSEOUT: false
};

// Custom config for tabbed hero containers
const tabbedHeroConfig = {
  ...TabbedNavConfig,
  OPEN_FIRST_TAB: false,
  CLOSE_TAB_ON_MOUSEOUT: true
};

// Function to determine the appropriate config based on container context
function getConfigForContainer(container) {
  // Check if container has closest ancestor with [data-tabbed-hero="true"]
  const heroAncestor = container.getAttribute('data-tabbed-hero') === 'true'   ? container : container.closest('[data-tabbed-dropdown="true"]');
  
  if (heroAncestor) {
    return tabbedHeroConfig;
  }
  
  // Check for specific data-instance attributes (if you still want to support them)
  // const instance = container.getAttribute('data-instance');
  // if (instance === 'two') {
  //   // You can still have specific instance configs if needed
  //   return {
  //     ...TabbedNavConfig,
  //     OPEN_FIRST_TAB: false,
  //     CLOSE_TAB_ON_MOUSEOUT: true
  //   };
  // }
  
  // Default config
  return TabbedNavConfig;
}

class TabbedNavigation {
  constructor(container, config = TabbedNavConfig) {
    this.container = container;
    this.config = config;
    this.activeItem = null;
    this.isDesktop = window.innerWidth >= this.config.DESKTOP_BREAKPOINT;
    this.hasMouseListeners = false;
    this.megamenuParent = null;
    this.megamenuObserver = null;
    
    // Initialize bound handlers - these will be reused across reinitializations
    this.mouseEnterHandler = this.handleMouseEnter.bind(this);
    this.mouseLeaveHandler = this.handleMouseLeave.bind(this);
    this.resizeHandler = this.debounce(this.handleResize.bind(this), this.config.DEBOUNCE_DELAY);
    this.handleMegamenuToggle = this.handleMegamenuToggle.bind(this);
    
    this.init();
  }

  init() {
    // Cache DOM elements fresh each time
    this.navItems = [...this.container.querySelectorAll('.dwc-tabbed-nav-list__li')];
    this.elementsCache = new Map();
    
    this.cacheElements();
    this.setupAccessibility();
    this.setupMegamenuObserver();
    this.bindEvents();

    // Set initial state
    this.updateLayout();
    this.setBackText()

    // Show first item on page load only if desktop, not nested, and OPEN_FIRST_TAB is true
    if (this.navItems.length > 0 && this.isDesktop && this.config.OPEN_FIRST_TAB) {
      setTimeout(() => {
        const firstItem = this.navItems[0];

        // Count how many .dwc-tabbed-nav-container ancestors this.container has
        let count = 0;
        let current = this.container.parentElement;
        while (current) {
          if (current.classList?.contains('dwc-tabbed-nav-container')) {
            count++;
          }
          current = current.parentElement;
        }

        const isNested = count >= 1;

        if (!isNested && this.elementsCache.has(firstItem)) {
          this.showTab(firstItem);
        }
      }, 0);
    }
  }

  // Setup observer for megamenu parent
  setupMegamenuObserver() {
    // Find the .brx-has-megamenu parent
    this.megamenuParent = this.container.closest('.brx-has-megamenu');
    
    if (this.megamenuParent) {
      // Set initial inert state
      this.updateInertState();
      
      // Create a MutationObserver to watch for class changes
      this.megamenuObserver = new MutationObserver((mutations) => {
        mutations.forEach((mutation) => {
          if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
            this.updateInertState();
          }
        });
      });
      
      // Start observing the megamenu parent for class changes
      this.megamenuObserver.observe(this.megamenuParent, {
        attributes: true,
        attributeFilter: ['class']
      });
    }
  }

  // Update inert state based on megamenu open/closed state
  updateInertState() {
    if (!this.megamenuParent) return;
    
    const isOpen = this.megamenuParent.classList.contains('open');
    
    if (isOpen) {
      this.container.removeAttribute('inert');
    } else {
      this.container.setAttribute('inert', '');
    }
  }

  // Handle megamenu toggle (for manual triggering if needed)
  handleMegamenuToggle() {
    this.updateInertState();
  }

  // Method to clean up event listeners and reset state
  cleanup() {
    // Remove all event listeners
    this.container.removeEventListener('click', this.handleClick);
    this.container.removeEventListener('keydown', this.handleKeydown);
    this.container.removeEventListener('focus', this.handleFocus);
    
    // Remove mouse listeners if they exist
    if (this.hasMouseListeners) {
      this.container.removeEventListener('mouseenter', this.mouseEnterHandler, true);
      this.container.removeEventListener('mouseleave', this.mouseLeaveHandler, true);
    }
    
    // Remove resize listener
    window.removeEventListener('resize', this.resizeHandler);
    
    // Clean up megamenu observer
    if (this.megamenuObserver) {
      this.megamenuObserver.disconnect();
      this.megamenuObserver = null;
    }
    
    // Reset state
    this.activeItem = null;
    this.hasMouseListeners = false;
    this.megamenuParent = null;
    
    // Clear caches
    if (this.elementsCache) {
      this.elementsCache.clear();
    }
    this.navItems = [];
    
    // Reset all tab states and accessibility attributes
    const allNavItems = this.container.querySelectorAll('.dwc-tabbed-nav-list__li');
    allNavItems.forEach(item => {
      const button = item.querySelector('.dwc-tabbed-nav-list__li__button') || 
                    item.querySelector('.dwc-tabbed-nav-list__li__btn-txt');
      const content = item.querySelector('.dwc-tabbed-nav-list__li__content');
      
      // Remove classes
      item.classList.remove('active');
      
      // Reset button attributes
      if (button) {
        button.removeAttribute('role');
        button.removeAttribute('aria-controls');
        button.removeAttribute('aria-expanded');
        button.removeAttribute('aria-selected');
        button.removeAttribute('tabindex');
        button.removeAttribute('id');
      }
      
      // Reset content attributes
      if (content) {
        content.removeAttribute('role');
        content.removeAttribute('aria-labelledby');
        content.removeAttribute('id');
        content.removeAttribute('aria-hidden');
        content.style.maxHeight = '';
      }
      
      // Remove data attributes
      item.removeAttribute('data-tab-index');
    });
    
    // Reset nav list role
    const navList = this.container.querySelector('.dwc-tabbed-nav-list');
    if (navList) {
      navList.removeAttribute('role');
    }
    
    // Remove inert attribute
    this.container.removeAttribute('inert');
  }

  // Public method to reinitialize the navigation
  reinitialize() {
    this.cleanup();
    this.isDesktop = window.innerWidth >= this.config.DESKTOP_BREAKPOINT; // Use config breakpoint
    this.init();
  }

  // Cache DOM elements to avoid repeated queries
  cacheElements() {
    const uniqueIdBase = `tab-${Date.now()}`;
    
    this.navItems.forEach((item, index) => {
      const button = item.querySelector('.dwc-tabbed-nav-list__li__button') || 
                    item.querySelector('.dwc-tabbed-nav-list__li__btn-txt');
      const content = item.querySelector('.dwc-tabbed-nav-list__li__content');
      
      this.elementsCache.set(item, {
        button,
        content,
        uniqueId: `${uniqueIdBase}-${index}`,
        index
      });
    });
  }

  setupAccessibility() {
    const navList = this.container.querySelector('.dwc-tabbed-nav-list');
    if (!navList) return;
    
    navList.setAttribute('role', 'tablist');
    
    for (const [item, cache] of this.elementsCache) {
      const { button, content, uniqueId, index } = cache;
      
      if (button) {
        button.setAttribute('role', 'tab');
        button.setAttribute('aria-controls', `${uniqueId}-panel`);
        button.setAttribute('aria-expanded', 'false');
        button.setAttribute('aria-selected', 'false');
        button.setAttribute('tabindex', '0');
        button.setAttribute('id', `${uniqueId}-tab`);
      }
      
      if (content) {
        content.setAttribute('role', 'tabpanel');
        content.setAttribute('aria-labelledby', `${uniqueId}-tab`);
        content.setAttribute('id', `${uniqueId}-panel`);
        content.setAttribute('aria-hidden', 'true');
      }
      
      item.setAttribute('data-tab-index', index);
    }
  }

  bindEvents() {
    // Store bound handlers for cleanup
    this.handleClick = this.handleClick.bind(this);
    this.handleKeydown = this.handleKeydown.bind(this);
    this.handleFocus = this.handleFocus.bind(this);
    
    // Single event delegation for better performance
    this.container.addEventListener('click', this.handleClick);
    this.container.addEventListener('keydown', this.handleKeydown);
    this.container.addEventListener('focus', this.handleFocus, true);
    
    // Passive resize listener for better performance
    window.addEventListener('resize', this.resizeHandler, { passive: true });
    
    this.updateMouseListeners();
  }

  updateMouseListeners() {
    if (this.isDesktop && !this.hasMouseListeners) {
      this.container.addEventListener('mouseenter', this.mouseEnterHandler, true);
      this.container.addEventListener('mouseleave', this.mouseLeaveHandler, true);
      this.hasMouseListeners = true;
    } else if (!this.isDesktop && this.hasMouseListeners) {
      this.container.removeEventListener('mouseenter', this.mouseEnterHandler, true);
      this.container.removeEventListener('mouseleave', this.mouseLeaveHandler, true);
      this.hasMouseListeners = false;
    }
  }

  // Helper method to get sibling nav items (nav items in the same container level)
  getSiblingNavItems(navItem) {
    // Find the direct parent container of this nav item
    const navItemContainer = navItem.closest('.dwc-tabbed-nav-list');
    if (!navItemContainer) return [];
    
    // Get all nav items that are direct children of the same container
    const siblingItems = [...navItemContainer.querySelectorAll('.dwc-tabbed-nav-list__li')];
    
    // Filter to only include items that are siblings (not the navItem itself)
    // and check if they belong to any TabbedNavigation instance
    return siblingItems.filter(item => item !== navItem);
  }

  handleFocus(e) {
    if (!this.isDesktop) return;

    const button = e.target.closest('[role="tab"]');
    if (!button) return;
    
    const navItem = button.closest('.dwc-tabbed-nav-list__li');
    if (!navItem || !this.elementsCache.has(navItem)) return;
    
    this.handleTabInteraction(navItem, 'focus');
  }

  handleClick(e) {
    const navItem = e.target.closest('.dwc-tabbed-nav-list__li');
    if (!navItem || !this.elementsCache.has(navItem)) return;
    
    const button = e.target.closest('.dwc-tabbed-nav-list__li__button, .dwc-tabbed-nav-list__li__btn-txt');
    if (!button) return;
    
    e.preventDefault();
    this.handleTabInteraction(navItem, 'click');
  }

  handleKeydown(e) {
    const button = e.target.closest('[role="tab"]');
    if (!button) return;
    
    const navItem = button.closest('.dwc-tabbed-nav-list__li');
    if (!navItem || !this.elementsCache.has(navItem)) return;
    
    const { index: currentIndex } = this.elementsCache.get(navItem);
    
    switch (e.key) {
      case 'Enter':
      case ' ':
        e.preventDefault();
        this.handleTabInteraction(navItem, 'keyboard');
        break;
      case 'ArrowRight':
      case 'ArrowDown':
        e.preventDefault();
        this.focusNextTab(currentIndex);
        break;
      case 'ArrowLeft':
      case 'ArrowUp':
        e.preventDefault();
        this.focusPrevTab(currentIndex);
        break;
      case 'Home':
        e.preventDefault();
        this.focusTab(0);
        break;
      case 'End':
        e.preventDefault();
        this.focusTab(this.navItems.length - 1);
        break;
    }
  }
  
  handleMouseEnter(e) {
    if (!this.isDesktop) return;
    
    const navItem = e.target.closest('.dwc-tabbed-nav-list__li') || 
                   (e.target.matches('.dwc-tabbed-nav-list__li') ? e.target : null);
    if (!navItem || !this.elementsCache.has(navItem)) return;
    
    this.handleTabInteraction(navItem, 'hover');
  }

  handleMouseLeave(e) {
    if (!this.isDesktop) return;
    
    // Only close tabs if CLOSE_TAB_ON_MOUSEOUT is enabled
    if (this.config.CLOSE_TAB_ON_MOUSEOUT) {
      // Check if mouse is leaving the entire container
      const rect = this.container.getBoundingClientRect();
      const { clientX, clientY } = e;
      
      // Check if mouse position is outside the container bounds
      const isOutsideContainer = (
        clientX < rect.left || 
        clientX > rect.right || 
        clientY < rect.top || 
        clientY > rect.bottom
      );
      
      if (isOutsideContainer) {
        this.hideAllTabs();
      }
    }
    // If CLOSE_TAB_ON_MOUSEOUT is false, keep content visible (original behavior)
  }

  // Unified method to handle all tab interactions with nesting support
  handleTabInteraction(navItem, interactionType) {
    if (!this.elementsCache.has(navItem)) return;
    
    if (this.isDesktop) {
      // Simply hide siblings and show the target - works for all scenarios
      this.hideSiblingTabs(navItem);
      this.showTab(navItem);
    } else {
      // Mobile accordion behavior
      if (interactionType === 'click' || interactionType === 'keyboard') {
        this.toggleTabMobile(navItem);
      }
    }
  }

  // Hide only sibling tabs (same container level)
  hideSiblingTabs(navItem) {
   // console.log('Nav Item: ', navItem)
    const siblings = this.getSiblingNavItems(navItem);
    
    siblings.forEach(sibling => {
      
         //console.log('sibling: ', sibling)
      // Only hide siblings that have the active class to avoid affecting other instances
      if (sibling.classList.contains('active')) {
        this.hideSiblingTab(sibling);
      }
    });
  }

  // Hide a sibling tab (used when we know it's a sibling from another instance)
  hideSiblingTab(navItem) {
    const button = navItem.querySelector('.dwc-tabbed-nav-list__li__button') || 
                  navItem.querySelector('.dwc-tabbed-nav-list__li__btn-txt');
    const content = navItem.querySelector('.dwc-tabbed-nav-list__li__content');
    
    if (content && button) {
      content.setAttribute('aria-hidden', 'true');
      button.setAttribute('aria-expanded', 'false');
      button.setAttribute('aria-selected', 'false');
      navItem.classList.remove('active');
      
      // Also handle mobile accordion collapse
      if (!this.isDesktop) {
        content.style.maxHeight = '0';
      }
    }
  }

  // Mobile-specific toggle behavior with proper nesting support
  toggleTabMobile(navItem) {
    if (!this.elementsCache.has(navItem)) return;
    
    const isActive = navItem.classList.contains('active');
    
    if (isActive) {
      this.collapseTab(navItem);
    } else {
      // Close sibling tabs and open this one
      this.hideSiblingTabs(navItem);
      this.expandTab(navItem);
    }
  }

  toggleTab(navItem) {
    if (!this.elementsCache.has(navItem)) return;
    
    if (this.isDesktop) {
      this.handleTabInteraction(navItem, 'click');
    } else {
      this.toggleTabMobile(navItem);
    }
  }

  showTab(navItem) {
    if (!this.elementsCache.has(navItem)) return;
    
    const { button, content } = this.elementsCache.get(navItem);
    
    if (content && button) {
      content.setAttribute('aria-hidden', 'false');
      button.setAttribute('aria-expanded', 'true');
      button.setAttribute('aria-selected', 'true');
      navItem.classList.add('active');
    }
    
    this.setActiveTab(navItem);
  }

  hideTab(navItem) {
    if (!this.elementsCache.has(navItem)) return;
    
    const { button, content } = this.elementsCache.get(navItem);
    
    if (content && button) {
      content.setAttribute('aria-hidden', 'true');
      button.setAttribute('aria-expanded', 'false');
      button.setAttribute('aria-selected', 'false');
      navItem.classList.remove('active');
    }
    
    // Only clear activeItem if it belongs to THIS navigation instance
    // and it's the exact item being hidden
    if (this.activeItem === navItem && this.navItems.includes(navItem)) {
      this.activeItem = null;
    }
  }

  expandTab(navItem) {
    if (!this.elementsCache.has(navItem)) return;
    
    // Close sibling tabs
    this.hideSiblingTabs(navItem);
    
    this.showTab(navItem);
    
    // Smooth height animation
    const { content } = this.elementsCache.get(navItem);
    if (content) {
      const height = content.scrollHeight;
      content.style.maxHeight = `${height}px`;
    }
  }

  collapseTab(navItem) {
    if (!this.elementsCache.has(navItem)) return;
    
    this.hideTab(navItem);
    
    const { content } = this.elementsCache.get(navItem);
    if (content) {
      content.style.maxHeight = '0';
    }
  }

  hideAllTabs() {
    this.navItems.forEach(item => this.hideTab(item));
  }

  setActiveTab(navItem) {
    // Only update aria-selected for tabs in THIS navigation instance
    this.navItems.forEach(item => {
      const cache = this.elementsCache.get(item);
      if (cache?.button) {
        cache.button.setAttribute('aria-selected', item === navItem ? 'true' : 'false');
      }
    });
    
    // Only set activeItem if this navItem belongs to THIS navigation instance
    if (this.navItems.includes(navItem)) {
      this.activeItem = navItem;
    }
  }

  focusTab(index) {
    if (index >= 0 && index < this.navItems.length) {
      const cache = this.elementsCache.get(this.navItems[index]);
      if (cache?.button) {
        cache.button.focus();
      }
    }
  }

  focusNextTab(currentIndex) {
    const nextIndex = (currentIndex + 1) % this.navItems.length;
    this.focusTab(nextIndex);
  }

  focusPrevTab(currentIndex) {
    const prevIndex = currentIndex === 0 ? this.navItems.length - 1 : currentIndex - 1;
    this.focusTab(prevIndex);
  }

  handleResize() {
    const wasDesktop = this.isDesktop;
    this.isDesktop = window.innerWidth >= this.config.DESKTOP_BREAKPOINT; // Use config breakpoint
    
    if (wasDesktop !== this.isDesktop) {
      this.updateMouseListeners();
      this.updateLayout();
    }
  }

  updateLayout() {
    const currentlyActive = this.activeItem;

    // Reset all states
    this.hideAllTabs();

    // Clear inline styles
    this.navItems.forEach(item => {
      const cache = this.elementsCache.get(item);
      if (cache?.content) {
        cache.content.style.maxHeight = '';
      }
    });

    if (this.isDesktop) {
      // Determine if this tab instance is nested
      let count = 0;
      let current = this.container.parentElement;
      while (current) {
        if (current.classList?.contains('dwc-tabbed-nav-container')) {
          count++;
        }
        current = current.parentElement;
      }
      const isNested = count >= 1;

      if (!isNested && this.config.OPEN_FIRST_TAB) {
        if (currentlyActive) {
          this.showTab(currentlyActive);
        } else if (this.navItems.length > 0) {
          this.showTab(this.navItems[0]);
        }
      }
    }
  }

  setBackText(){
    let tabElement = document.querySelector('.dwc-tabbed-nav-container');
    let tabBackText = tabElement.getAttribute('data-back-text')
    document.querySelectorAll('.dwc-tabbed-nav-list__li__button').forEach(tabBtn => {
        const textContent = tabBtn.textContent.trim();
        const tabIcon = tabBtn.querySelector('.dwc-tabbed-nav-list__li__arrow-icon');
        if (tabIcon) {
            tabIcon.setAttribute('data-text', textContent);
            tabIcon.setAttribute('data-back-text', tabBackText);
        }
    });
  }

  // Simple debounce for resize
  debounce(func, wait) {
    let timeout;
    return function executedFunction(...args) {
      const later = () => {
        clearTimeout(timeout);
        func(...args);
      };
      clearTimeout(timeout);
      timeout = setTimeout(later, wait);
    };
  }

  // Public method to destroy the instance completely
  destroy() {
    this.cleanup();
    // Additional cleanup can be added here if needed
  }

  // Public method to manually update inert state (if needed)
  updateInert() {
    this.updateInertState();
  }
}

// Simplified initialization on DOM ready
document.addEventListener('DOMContentLoaded', function() {
  const tabbedNavContainers = document.querySelectorAll('.dwc-tabbed-nav-container');
  
  tabbedNavContainers.forEach(container => {
    // Skip if this container is nested inside another dwc-tabbed-nav-container
    const parentNavContainer = container.parentElement.closest('.dwc-tabbed-nav-container');
    if (parentNavContainer) {
      return; // Skip nested containers
    }
    
    // Get the appropriate config for this container
    const config = getConfigForContainer(container);
    
    // Initialize with the determined config
    new TabbedNavigation(container, config);
  });
});

// For dynamic content - Updated version
window.initTabbedNav = function(container, customConfig = null) {
  if (container && container.classList.contains('dwc-tabbed-nav-container')) {
    // Check if this is a nested container
    const parentNavContainer = container.parentElement.closest('.dwc-tabbed-nav-container');
    if (parentNavContainer) {
      return null; // Don't initialize nested containers
    }
    
    // Use custom config if provided, otherwise determine config automatically
    const config = customConfig || getConfigForContainer(container);
    return new TabbedNavigation(container, config);
  }
  return null;
};

/*
// TO REINITIALIZE USE THIS
// Create an instance of TabbedNavigation
const tabbedNav = new TabbedNavigation(document.querySelector('.dwc-tabbed-nav-container'));

// Call reinitialize on the instance
tabbedNav.reinitialize();

// TO USE WITH CUSTOM CONFIG
const customConfig = {
  DESKTOP_BREAKPOINT: 768,
  DEBOUNCE_DELAY: 300,
  OPEN_FIRST_TAB: false,  // This will prevent first tab from opening automatically
  CLOSE_TAB_ON_MOUSEOUT: true  // This will close tabs when mouse leaves container
};
const tabbedNavCustom = new TabbedNavigation(document.querySelector('.dwc-tabbed-nav-container'), customConfig);

// TO UPDATE ON URL CHANGE
function onUrlChange() {
    setTimeout(function () {
const tabbedNav = new TabbedNavigation(document.querySelector('.dwc-tabbed-nav-container'));
tabbedNav.reinitialize();

    }, 1500); // Delay execution by 2 seconds
}

// Listen for popstate event (history navigation)
window.addEventListener('popstate', onUrlChange);

// Listen for hashchange event (URL hash change)
window.addEventListener('hashchange', onUrlChange);

// Handle pushState and replaceState
(function (history) {
    const pushState = history.pushState;
    const replaceState = history.replaceState;

    history.pushState = function (state) {
        if (typeof history.onpushstate == "function") {
            history.onpushstate({ state: state });
        }
        const result = pushState.apply(history, arguments);
        onUrlChange(); // Call the function when pushState is used
        return result;
    };

    history.replaceState = function (state) {
        if (typeof history.onreplacestate == "function") {
            history.onreplacestate({ state: state });
        }
        const result = replaceState.apply(history, arguments);
        onUrlChange(); // Call the function when replaceState is used
        return result;
    };
})(window.history);

*/

console.log('%c<Jamie Sheehan Web Design Mega Menu Pro Tabbed Navigation v1.0>', 'color: #b388eb');

Meet Team Navarik – Jocelyne Kaminski

August 22, 2023

In this post...

All Articles

We are pleased to introduce Jocelyne Kaminski, our resident analyst with multi-faceted expertise in data and global maritime intelligence.  

Jocelyne kickstarted her career with Navarik in 2015 as a data entry specialist. A strong work ethic, alongside her love for learning resulted in a series of promotions to her current role as a Data Reporting Manager. Throughout her career, Jocelyne played a pivotal role in pioneering the tradeflow reports and now manages her own team of analysts as they commit to providing the market with data-driven solutions.   

Jocelyne’s contributions to Navarik have been nothing short of significant. Within 6 months of starting at Navarik, she began assisting with report development. Mainly, she focuses on US-centric import, export, and interregional movements for refined products, fuel oils, and feedstocks. Her time and seasoned experience shaped the various report iterations offered to the market, allowing for targeted expansion of product coverage from single to multiple regions. Currently, as Data Reporting Manager, Jocelyne is responsible for releasing weekly market data and mentoring a team of eager, budding analysts.  

In addition to being a superstar Data Reporting Manager, Jocelyne wears many hats within Navarik. When she isn’t reporting on market data, she can be found assisting Navarik Support in handling time-sensitive customer requests. As a stickler for details, she also manages Navarik catalogs meticulously to ensure our clients have access to the most comprehensive and up-to-date data for their use. The sum of her experiences, alongside her patience and supportive nature have played an integral role as not only a manager but also an effective mentor.  

At the core of Jocelyne’s passion lies the data she works with. One of the most appealing elements of her work is witnessing how global events impact the data. With these insights, she is able to piece together powerful, compelling stories from numbers and trends.  

“What I find most fascinating are the stories, meanings, and significance we’re able to extract about our world.” 

While it can be difficult at times staying informed with world developments and anticipating impacts on trade data, she finds this challenge particularly alluring. Besides her genuine love for the data, Jocelyne’s strong work ethic has also been an important driving force for her career.  

A tireless commitment to data quality, alongside her data prowess and a keen eye for detail has built the foundations for the deep industry knowledge that Jocelyne brings to Navarik, whether it be for fun facts at team meetings or business insight. An appreciation for continuous learning allowed her to thrive in situations that both utilize old skills and challenge her to acquire new ones. Abiding by the values of hard work, dedication, and accountability allows her to take great pride in her work. When asked, her team describes her as knowledgeable, approachable, and a tremendously supportive mentor. Mostly, Jocelyne emphasizes the importance of goal setting and adopting an opportunistic attitude toward challenges.  

With her expertise, we ask Jocelyne what advice she would offer to those pursuing a similar career. By way of technical competencies, acquiring knowledge of database management, novice coding expertise (SQL, R), and basic data visualization tools (PowerBI, Tableau) are a must. Apart from technical skills, she emphasizes the importance of continuous learning and keeping up to date with advancements in the data industry. Lastly, she recommends seeking a supportive environment that acknowledges the importance of mental health.  

Serious business aside, Jocelyne is a self-professed data nerd who prefers Star Wars over Star Trek. Besides tracking maritime movements, Jocelyne is equally fascinated by discoveries revealed by the James Webb Space Telescope. As a musician of multiple talents across the guitar, flute, piano, snare drum, and practice chanter, she used to arrange local punk shows in her hometown of Lethbridge, Alberta. When she isn’t indulging musically, she can be found playing Fallout, Portal, Call of Duty, or other RPGs (role-playing games). She also enjoys spending time with friends and family, but it would be most criminal to forget time with her beloved cat, Zeus. Currently, her focus is to improve her culinary skills, and has a grand reputation for putting up lavish Ukrainian Christmas feasts.  

We are thrilled to celebrate Jocelyne’s achievements and contributions to Navarik over the last 8 years. Our company is privileged to witness growth from her dedication and passion. Congratulations on all you have accomplished, Jocelyne – we look forward to your advancing career!