or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

buttons.mddata-display.mdforms.mdindex.mdlayout.mdnavigation.mdoverlays.mdpickers.mdstyling-theming.mdutilities.md
tile.json

utilities.mddocs/

Utilities & Helpers

Utility functions for DOM manipulation, focus management, performance optimization, and common operations. These utilities provide the foundation for Fluent UI React components and can be used independently.

Capabilities

DOM & Browser Utilities

Core utilities for DOM manipulation and browser detection.

/**
 * Check if DOM is available (not server-side)
 * @returns Whether DOM is available
 */
function canUseDOM(): boolean;

/**
 * Get document reference safely
 * @param element - Element to get document from
 * @returns Document object or undefined
 */
function getDocument(element?: HTMLElement): Document | undefined;

/**
 * Get window reference safely
 * @param element - Element to get window from
 * @returns Window object or undefined
 */
function getWindow(element?: HTMLElement): Window | undefined;

/**
 * Get element bounding rectangle
 * @param element - Element to measure
 * @returns Rectangle object
 */
function getRect(element: HTMLElement): IRectangle;

/**
 * Get scrollbar width for the current browser
 * @returns Scrollbar width in pixels
 */
function getScrollbarWidth(): number;

/**
 * Check if element contains another element
 * @param parent - Parent element
 * @param child - Child element
 * @returns Whether parent contains child
 */
function elementContains(parent: HTMLElement, child: HTMLElement): boolean;

/**
 * Check if element has specific attribute
 * @param element - Element to check
 * @param attribute - Attribute name
 * @returns Whether element has attribute
 */
function elementContainsAttribute(element: HTMLElement, attribute: string): boolean;

/**
 * Find scrollable parent element
 * @param startingElement - Starting element
 * @returns Scrollable parent or null
 */
function findScrollableParent(startingElement: HTMLElement): HTMLElement | null;

/**
 * Check if element has horizontal overflow
 * @param element - Element to check
 * @returns Whether element has horizontal overflow
 */
function hasHorizontalOverflow(element: HTMLElement): boolean;

/**
 * Check if element has vertical overflow
 * @param element - Element to check
 * @returns Whether element has vertical overflow
 */
function hasVerticalOverflow(element: HTMLElement): boolean;

/**
 * Check if element has any overflow
 * @param element - Element to check
 * @returns Whether element has overflow
 */
function hasOverflow(element: HTMLElement): boolean;

/**
 * Check if element is visible
 * @param element - Element to check
 * @returns Whether element is visible
 */
function isElementVisible(element: HTMLElement): boolean;

/**
 * Check if element is tabbable
 * @param element - Element to check
 * @returns Whether element is tabbable
 */
function isElementTabbable(element: HTMLElement): boolean;

/**
 * Allow scrolling on element
 * @param element - Element to allow scrolling on
 * @param events - Event group for cleanup
 */
function allowScrollOnElement(element: HTMLElement, events: EventGroup): void;

/**
 * Disable body scrolling
 */
function disableBodyScroll(): void;

/**
 * Enable body scrolling
 */
function enableBodyScroll(): void;

/**
 * Check if browser is IE11
 * @returns Whether browser is IE11
 */
function isIE11(): boolean;

/**
 * Check if platform is iOS
 * @returns Whether platform is iOS
 */
function isIOS(): boolean;

/**
 * Check if platform is Mac
 * @returns Whether platform is Mac
 */
function isMac(): boolean;

interface IRectangle {
  left: number;
  top: number;
  width: number;
  height: number;
  right?: number;
  bottom?: number;
}

Focus Management Utilities

Utilities for managing focus and keyboard navigation.

/**
 * Focus element asynchronously
 * @param element - Element to focus
 * @param callback - Optional callback
 * @returns Whether focus was successful
 */
function focusAsync(element: HTMLElement, callback?: () => void): boolean;

/**
 * Focus first child element
 * @param rootElement - Root element to search within
 * @returns Whether focus was successful
 */
function focusFirstChild(rootElement: HTMLElement): boolean;

/**
 * Get first focusable element
 * @param rootElement - Root element to search within
 * @param currentElement - Current element
 * @param includeElementsInFocusZones - Include focus zone elements
 * @returns First focusable element or null
 */
function getFirstFocusable(
  rootElement: HTMLElement,
  currentElement?: HTMLElement,
  includeElementsInFocusZones?: boolean
): HTMLElement | null;

/**
 * Get first tabbable element
 * @param rootElement - Root element to search within
 * @param currentElement - Current element
 * @param includeElementsInFocusZones - Include focus zone elements
 * @returns First tabbable element or null
 */
function getFirstTabbable(
  rootElement: HTMLElement,
  currentElement?: HTMLElement,
  includeElementsInFocusZones?: boolean
): HTMLElement | null;

/**
 * Get last focusable element
 * @param rootElement - Root element to search within
 * @param currentElement - Current element
 * @param includeElementsInFocusZones - Include focus zone elements
 * @returns Last focusable element or null
 */
function getLastFocusable(
  rootElement: HTMLElement,
  currentElement?: HTMLElement,
  includeElementsInFocusZones?: boolean
): HTMLElement | null;

/**
 * Get last tabbable element
 * @param rootElement - Root element to search within
 * @param currentElement - Current element
 * @param includeElementsInFocusZones - Include focus zone elements
 * @returns Last tabbable element or null
 */
function getLastTabbable(
  rootElement: HTMLElement,
  currentElement?: HTMLElement,
  includeElementsInFocusZones?: boolean
): HTMLElement | null;

/**
 * Get next focusable element
 * @param rootElement - Root element to search within
 * @param currentElement - Current element
 * @param checkNode - Check node function
 * @param suppressParentTraversal - Suppress parent traversal
 * @returns Next focusable element or null
 */
function getNextElement(
  rootElement: HTMLElement,
  currentElement: HTMLElement,
  checkNode?: (element: HTMLElement) => boolean,
  suppressParentTraversal?: boolean
): HTMLElement | null;

/**
 * Get previous focusable element
 * @param rootElement - Root element to search within
 * @param currentElement - Current element
 * @param checkNode - Check node function
 * @param suppressChildTraversal - Suppress child traversal
 * @returns Previous focusable element or null
 */
function getPreviousElement(
  rootElement: HTMLElement,
  currentElement: HTMLElement,
  checkNode?: (element: HTMLElement) => boolean,
  suppressChildTraversal?: boolean
): HTMLElement | null;

/**
 * Check if focus should wrap
 * @param element - Element to check
 * @param noWrapDataAttribute - No wrap data attribute
 * @returns Whether focus should wrap
 */
function shouldWrapFocus(element: HTMLElement, noWrapDataAttribute: string): boolean;

/**
 * Check if element contains focus
 * @param element - Element to check
 * @returns Whether element contains focus
 */
function doesElementContainFocus(element: HTMLElement): boolean;

/**
 * Check if element is a focus zone
 * @param element - Element to check
 * @returns Whether element is focus zone
 */
function isElementFocusZone(element?: HTMLElement): boolean;

/**
 * Check if element is a focus sub-zone
 * @param element - Element to check
 * @returns Whether element is focus sub-zone
 */
function isElementFocusSubZone(element?: HTMLElement): boolean;

Selection Utilities

Utilities for managing selection state and operations.

/**
 * Selection change event constant
 */
const SELECTION_CHANGE: string;

/**
 * Selection manager class
 */
class Selection implements ISelection {
  constructor(options?: ISelectionOptions);
  
  /** Get number of selected items */
  getSelectedCount(): number;
  
  /** Get array of selected items */
  getSelection(): any[];
  
  /** Check if index is selected */
  isIndexSelected(index: number): boolean;
  
  /** Check if all items are selected */
  isAllSelected(): boolean;
  
  /** Set all selected state */
  setAllSelected(isAllSelected: boolean): void;
  
  /** Set index selected state */
  setIndexSelected(index: number, isSelected: boolean, shouldAnchor?: boolean): void;
  
  /** Select to index */
  selectToIndex(index: number, clearSelection?: boolean): void;
  
  /** Toggle index selected */
  toggleIndexSelected(index: number): void;
  
  /** Toggle all selected */
  toggleAllSelected(): void;
  
  /** Toggle range selected */
  toggleRangeSelected(fromIndex: number, count: number): void;
}

interface ISelection {
  canSelectItem?: (item: any, index?: number) => boolean;
  getKey?: (item: any, index?: number) => string;
  getSelectedCount(): number;
  getSelection(): any[];
  isIndexSelected(index: number): boolean;
  isAllSelected(): boolean;
  setAllSelected(isAllSelected: boolean): void;
  setIndexSelected(index: number, isSelected: boolean, shouldAnchor?: boolean): void;
  selectToIndex(index: number, clearSelection?: boolean): void;
  toggleIndexSelected(index: number): void;
  toggleAllSelected(): void;
  toggleRangeSelected(fromIndex: number, count: number): void;
}

interface ISelectionOptions {
  onSelectionChanged?: () => void;
  getKey?: (item: any, index?: number) => string;
  canSelectItem?: (item: any, index?: number) => boolean;
  selectionMode?: SelectionMode;
}

enum SelectionDirection {
  horizontal = 0,
  vertical = 1,
}

enum SelectionMode {
  none = 0,
  single = 1,
  multiple = 2,
}

/**
 * Get all selected options from a dropdown
 * @param options - Dropdown options
 * @param selectedKeys - Selected keys
 * @returns Array of selected options
 */
function getAllSelectedOptions(options: any[], selectedKeys: (string | number)[]): any[];

Performance Utilities

Utilities for performance optimization including memoization and async operations.

/**
 * Create memoization function
 * @param fn - Function to memoize
 * @param maxCacheSize - Maximum cache size
 * @returns Memoized function
 */
function createMemoizer<T extends (...args: any[]) => any>(fn: T, maxCacheSize?: number): T;

/**
 * Memoize function results
 * @param fn - Function to memoize
 * @param resolver - Key resolver function
 * @returns Memoized function
 */
function memoize<T extends (...args: any[]) => any>(
  fn: T,
  resolver?: (...args: any[]) => string
): T;

/**
 * Function memoization decorator
 * @param target - Target object
 * @param key - Property key
 * @param descriptor - Property descriptor
 * @returns Modified descriptor
 */
function memoizeFunction<T extends Function>(
  target: any,
  key: string | number | symbol,
  descriptor: TypedPropertyDescriptor<T>
): TypedPropertyDescriptor<T> | void;

/**
 * Reset all memoizations
 */
function resetMemoizations(): void;

/**
 * Set memoization to use WeakMap
 * @param weakMap - Whether to use WeakMap
 */
function setMemoizeWeakMap(weakMap?: boolean): void;

/**
 * Async operation utilities
 */
class Async {
  constructor(parent?: object, onError?: (e: any) => void);
  
  /** Set timeout with cleanup */
  setTimeout(callback: () => void, duration: number): number;
  
  /** Set interval with cleanup */
  setInterval(callback: () => void, duration: number): number;
  
  /** Set immediate with cleanup */
  setImmediate(callback: () => void): number;
  
  /** Request animation frame with cleanup */
  requestAnimationFrame(callback: () => void): number;
  
  /** Throttle function calls */
  throttle<T extends Function>(func: T, wait?: number, options?: { leading?: boolean; trailing?: boolean }): T;
  
  /** Debounce function calls */
  debounce<T extends Function>(func: T, wait?: number, options?: { leading?: boolean; trailing?: boolean }): T;
  
  /** Dispose all async operations */
  dispose(): void;
}

/**
 * Safe request animation frame
 * @param callback - Callback function
 * @returns Request ID
 */
function safeRequestAnimationFrame(callback: () => void): number;

/**
 * Safe set timeout
 * @param callback - Callback function
 * @param duration - Timeout duration
 * @returns Timer ID
 */
function safeSetTimeout(callback: () => void, duration: number): number;

/**
 * Performance measurement utilities
 */
class FabricPerformance {
  /** Measure performance of function */
  static measure(name: string, fn: () => void): void;
  
  /** Get performance summary */
  static getSummary(): IPerfSummary;
  
  /** Reset performance measurements */
  static reset(): void;
}

interface IPerfSummary {
  [measureName: string]: IPerfMeasurement;
}

interface IPerfMeasurement {
  totalDuration: number;
  count: number;
  average: number;
  min: number;
  max: number;
}

Event Utilities

Event management and keyboard utilities.

/**
 * Event group for managing event listeners
 */
class EventGroup {
  constructor(parent: any);
  
  /** Add event listener */
  on(
    element: any,
    eventName: string,
    callback: (event?: any) => void,
    options?: boolean | AddEventListenerOptions
  ): void;
  
  /** Remove event listener */
  off(
    element?: any,
    eventName?: string,
    callback?: (event?: any) => void
  ): void;
  
  /** Raise event */
  raise(eventName: string, eventArgs?: any, bubbleEvent?: boolean): boolean | undefined;
  
  /** Dispose all event listeners */
  dispose(): void;
}

/**
 * Keyboard key codes
 */
const KeyCodes: {
  a: number;
  b: number;
  c: number;
  d: number;
  e: number;
  f: number;
  g: number;
  h: number;
  i: number;
  j: number;
  k: number;
  l: number;
  m: number;
  n: number;
  o: number;
  p: number;
  q: number;
  r: number;
  s: number;
  t: number;
  u: number;
  v: number;
  w: number;
  x: number;
  y: number;
  z: number;
  zero: number;
  one: number;
  two: number;
  three: number;
  four: number;
  five: number;
  six: number;
  seven: number;
  eight: number;
  nine: number;
  space: number;
  enter: number;
  tab: number;
  escape: number;
  backspace: number;
  delete: number;
  left: number;
  up: number;
  right: number;
  down: number;
  home: number;
  end: number;
  pageUp: number;
  pageDown: number;
  insert: number;
  f1: number;
  f2: number;
  f3: number;
  f4: number;
  f5: number;
  f6: number;
  f7: number;
  f8: number;
  f9: number;
  f10: number;
  f11: number;
  f12: number;
  semicolon: number;
  comma: number;
  period: number;
  dash: number;
  slash: number;
  graveAccent: number;
  openBracket: number;
  backSlash: number;
  closeBracket: number;
  singleQuote: number;
};

/**
 * Check if key code is directional
 * @param which - Key code
 * @returns Whether key is directional
 */
function isDirectionalKeyCode(which: number): boolean;

/**
 * Add directional key code
 * @param which - Key code to add
 */
function addDirectionalKeyCode(which: number): void;

/**
 * Remove directional key code
 * @param which - Key code to remove
 */
function removeDirectionalKeyCode(which: number): void;

/**
 * Get RTL-safe key code
 * @param which - Key code
 * @param isRTL - Whether RTL mode
 * @returns RTL-safe key code
 */
function getRTLSafeKeyCode(which: number, isRTL?: boolean): number;

General Utilities

Common utility functions for strings, objects, arrays, and IDs.

/**
 * Object assignment utility
 * @param target - Target object
 * @param sources - Source objects
 * @returns Merged object
 */
function assign<T>(target: T, ...sources: any[]): T;

/**
 * Object merging utility
 * @param target - Target object
 * @param sources - Source objects
 * @returns Merged object
 */
function merge<T>(target: Partial<T>, ...sources: Partial<T>[]): T;

/**
 * Omit properties from object
 * @param obj - Source object
 * @param exclusions - Properties to omit
 * @returns Object without omitted properties
 */
function omit<T>(obj: T, ...exclusions: (keyof T)[]): Partial<T>;

/**
 * Find item in array
 * @param array - Array to search
 * @param predicate - Predicate function
 * @returns Found item or undefined
 */
function find<T>(array: T[], predicate: (item: T, index?: number) => boolean): T | undefined;

/**
 * Find index in array
 * @param array - Array to search
 * @param predicate - Predicate function
 * @returns Found index or -1
 */
function findIndex<T>(array: T[], predicate: (item: T, index?: number) => boolean): number;

/**
 * Create array of specified length
 * @param size - Array size
 * @param defaultValue - Default value for items
 * @returns Created array
 */
function createArray<T>(size: number, defaultValue?: T): T[];

/**
 * Check if arrays are equal
 * @param array1 - First array
 * @param array2 - Second array
 * @param comparer - Optional comparer function
 * @returns Whether arrays are equal
 */
function arraysEqual<T>(
  array1: T[],
  array2: T[],
  comparer?: (item1: T, item2: T) => boolean
): boolean;

/**
 * Flatten nested arrays
 * @param array - Nested array
 * @returns Flattened array
 */
function flatten<T>(array: (T | T[])[]): T[];

/**
 * String formatting utility
 * @param format - Format string
 * @param values - Values to format
 * @returns Formatted string
 */
function format(format: string, ...values: any[]): string;

/**
 * Generate unique ID
 * @param prefix - ID prefix
 * @returns Unique ID string
 */
function getId(prefix?: string): string;

/**
 * Get person initials from name
 * @param name - Person's name
 * @param isRTL - Whether RTL mode
 * @param allowPhoneInitials - Allow phone number initials
 * @returns Person's initials
 */
function getInitials(name?: string, isRTL?: boolean, allowPhoneInitials?: boolean): string;

/**
 * Round number to precision
 * @param value - Number to round
 * @param precision - Decimal precision
 * @returns Rounded number
 */
function precisionRound(value: number, precision: number): number;

/**
 * Calculate decimal precision of number
 * @param value - Number to analyze
 * @returns Decimal precision
 */  
function calculatePrecision(value: number): number;

/**
 * Get values from object
 * @param obj - Object to get values from
 * @returns Array of values
 */
function values<T>(obj: { [key: string]: T }): T[];

/**
 * Warning callback function
 * @param message - Warning message
 */
function warn(message: string): void;

/**
 * Set warning callback
 * @param warningCallback - Warning callback function
 */
function setWarningCallback(warningCallback?: (message: string) => void): void;

Usage Examples:

import React, { useEffect, useRef } from "react";
import {
  getId,
  EventGroup,
  Async,
  Selection,
  SelectionMode,
  memoizeFunction,
  focusFirstChild,
  KeyCodes
} from "@fluentui/react";

function UtilitiesExample() {
  const eventGroupRef = useRef<EventGroup>();
  const asyncRef = useRef<Async>();
  const selectionRef = useRef<Selection>();
  const containerId = useRef(getId('container'));

  useEffect(() => {
    // Initialize utilities
    eventGroupRef.current = new EventGroup(this);
    asyncRef.current = new Async();
    selectionRef.current = new Selection({
      selectionMode: SelectionMode.multiple,
      onSelectionChanged: () => {
        console.log('Selection changed:', selectionRef.current?.getSelection());
      }
    });

    // Add event listeners
    const handleKeyDown = (event: KeyboardEvent) => {
      switch (event.which) {
        case KeyCodes.escape:
          console.log('Escape pressed');
          break;
        case KeyCodes.enter:
          console.log('Enter pressed');
          break;
      }
    };

    eventGroupRef.current.on(document, 'keydown', handleKeyDown);

    // Debounced function
    const debouncedSave = asyncRef.current.debounce(() => {
      console.log('Saving data...');
    }, 500);

    // Cleanup
    return () => {
      eventGroupRef.current?.dispose();
      asyncRef.current?.dispose();
    };
  }, []);

  // Memoized calculation
  const expensiveCalculation = memoizeFunction((data: number[]) => {
    console.log('Performing expensive calculation...');
    return data.reduce((sum, value) => sum + value * value, 0);
  });

  const handleFocusFirst = () => {
    const container = document.getElementById(containerId.current);
    if (container) {
      focusFirstChild(container);
    }
  };

  return (
    <div id={containerId.current}>
      <h2>Utilities Example</h2>
      
      <button onClick={handleFocusFirst}>
        Focus First Child
      </button>
      
      <input placeholder="First focusable input" />
      <input placeholder="Second focusable input" />
      
      <div>
        Calculation result: {expensiveCalculation([1, 2, 3, 4, 5])}
      </div>
    </div>
  );
}

Announced

Accessibility utility for announcing content changes to screen readers.

/**
 * Component for announcing content to screen readers
 * @param props - Announced properties
 * @returns JSX element for screen reader announcements
 */
function Announced(props: IAnnouncedProps): JSX.Element;

interface IAnnouncedProps {
  /** Message to announce */
  message?: string;
  /** ARIA live setting */
  'aria-live'?: 'off' | 'polite' | 'assertive';
  /** Custom styles */
  styles?: IAnnouncedStyles;
  /** Theme */
  theme?: ITheme;
}

Usage Examples:

import React, { useState } from "react";
import { Announced, DefaultButton } from "@fluentui/react";

function AnnouncedExample() {
  const [message, setMessage] = useState('');
  const [count, setCount] = useState(0);

  const handleClick = () => {
    const newCount = count + 1;
    setCount(newCount);
    setMessage(`Button clicked ${newCount} times`);
  };

  return (
    <div>
      <DefaultButton text="Click me" onClick={handleClick} />
      <Announced message={message} aria-live="polite" />
      <div>Count: {count}</div>
    </div>
  );
}

Marquee Selection

Utility for implementing marquee (drag-to-select) functionality over a collection of items.

/**
 * Marquee selection utility for drag-to-select functionality
 * @param props - Marquee selection properties
 * @returns JSX element for marquee selection
 */
function MarqueeSelection(props: IMarqueeSelectionProps): JSX.Element;

interface IMarqueeSelectionProps {
  /** Selection manager */
  selection: ISelection;
  /** Root element selector */
  rootProps?: React.HTMLAttributes<HTMLDivElement>;
  /** Whether marquee is enabled */
  isEnabled?: boolean;
  /** Drag origin callback */
  onShouldStartSelection?: (event: MouseEvent) => boolean;
  /** Custom styles */
  styles?: IMarqueeSelectionStyles;
  /** Theme */
  theme?: ITheme;
}

Usage Examples:

import React from "react";
import { MarqueeSelection, Selection, DetailsList } from "@fluentui/react";

function MarqueeSelectionExample() {
  const selection = new Selection({
    onSelectionChanged: () => {
      console.log('Selection changed:', selection.getSelection());
    },
  });

  const items = [
    { key: '1', name: 'Item 1', value: 'A' },
    { key: '2', name: 'Item 2', value: 'B' },
    { key: '3', name: 'Item 3', value: 'C' },
  ];

  const columns = [
    { key: 'name', name: 'Name', fieldName: 'name', minWidth: 100 },
    { key: 'value', name: 'Value', fieldName: 'value', minWidth: 100 },
  ];

  return (
    <MarqueeSelection selection={selection}>
      <DetailsList
        items={items}
        columns={columns}
        selection={selection}
        selectionMode={SelectionMode.multiple}
      />
    </MarqueeSelection>
  );
}

Drag Drop Helper

Utility class for implementing drag and drop functionality.

/**
 * Helper class for drag and drop operations
 */
class DragDropHelper {
  constructor(params: IDragDropHelperParams);
  
  /** Subscribe to drag/drop events */
  subscribe(
    element: HTMLElement,
    events: IDragDropEvent,
    target: IDragDropTarget
  ): () => void;
  
  /** Unsubscribe from events */
  unsubscribe(): void;
  
  /** Dispose of helper */
  dispose(): void;
}

interface IDragDropHelperParams {
  /** Selection manager */
  selection: ISelection;
  /** Minimum distance before drag starts */
  minimumPixelsForDrag?: number;
}

interface IDragDropEvent {
  /** Drag start callback */
  onDragStart?: (item?: any, itemIndex?: number, selectedItems?: any[], event?: MouseEvent) => void;
  /** Drag end callback */
  onDragEnd?: (item?: any, event?: DragEvent) => void;
  /** Drop callback */
  onDrop?: (item?: any, event?: DragEvent) => void;
}

interface IDragDropTarget {
  /** Whether drop is allowed */
  canDrop?: (dropContext?: IDragDropContext) => boolean;
  /** Drop callback */
  onDrop?: (item?: any, event?: DragEvent) => void;
  /** Drag enter callback */
  onDragEnter?: (item?: any, event?: DragEvent) => void;
  /** Drag leave callback */
  onDragLeave?: (item?: any, event?: DragEvent) => void;
}

Usage Examples:

import { DragDropHelper, Selection } from "@fluentui/react";

function DragDropExample() {
  const selection = new Selection();
  
  React.useEffect(() => {
    const dragDropHelper = new DragDropHelper({
      selection,
      minimumPixelsForDrag: 5
    });

    const element = document.getElementById('drag-container');
    if (element) {
      const unsubscribe = dragDropHelper.subscribe(
        element,
        {
          onDragStart: (item, itemIndex, selectedItems, event) => {
            console.log('Drag started:', item);
          },
          onDragEnd: (item, event) => {
            console.log('Drag ended:', item);
          }
        },
        {
          canDrop: () => true,
          onDrop: (item, event) => {
            console.log('Item dropped:', item);
          }
        }
      );

      return () => {
        unsubscribe();
        dragDropHelper.dispose();
      };
    }
  }, []);

  return (
    <div id="drag-container">
      <div>Drag me around!</div>
    </div>
  );
}