Skip to main content

User Interface Components

Overview

The Dynamic JSON Visualizer's user interface is built with modular React components that handle different aspects of data interaction. Each component focuses on a specific functionality while maintaining consistent styling and user experience patterns.

FileUpload Component

The FileUpload component serves as the primary entry point for getting JSON data into the application. It supports multiple input methods and provides comprehensive error handling.

Input Methods

The component supports four different ways to load JSON data:

1. File Upload (Drag & Drop or Browse)

const handleFileUpload = async (file: File) => {
try {
setError(null);
const content = await file.text();
const parsedData = parseJson(content);

if (!parsedData) {
setError('Invalid JSON format. Please provide a valid JSON file.');
return;
}

const result = await loadJsonData(parsedData, uniqueFields);
// Success feedback with detailed statistics
} catch (err) {
// Error handling
}
};

Features:

  • Drag and drop support - Visual feedback when dragging files over the drop zone
  • File browser - Traditional file input for selecting JSON files
  • File validation - Only accepts .json files
  • Visual state changes - Drop zone changes appearance during drag operations

2. Clipboard Paste

const handlePaste = async () => {
try {
const clipboardText = await navigator.clipboard.readText();
const parsedData = parseJson(clipboardText);
// Process clipboard JSON data
} catch (err) {
// Handle clipboard access errors
}
};

Uses the Clipboard API to read JSON data directly from the user's clipboard, enabling quick data import from other applications.

3. Manual Entry

const handleManualEntry = async () => {
const jsonStr = prompt('Enter JSON data:');
if (!jsonStr) return;

const parsedData = parseJson(jsonStr);
// Process manually entered JSON
};

Simple prompt-based input for small JSON datasets or quick testing.

4. Sample Data Loading

const handleSampleData = async () => {
try {
const response = await fetch('https://dummyjson.com/products');
const data = await response.json();
const products = data.products;

await loadJsonData(products);
await setImageField('thumbnail');
await setDisplayFields(["title", "description", "price", "brand", "category"]);
} catch (err) {
// Handle API errors
}
};

Loads sample product data from an external API and automatically configures the application for optimal display of that data type.

Smart Feedback System

The component provides detailed feedback about data loading operations:

let message = `Successfully loaded ${objectCount} ${objectCount === 1 ? 'object' : 'objects'}`;

if (uniqueFields.length > 0) {
message += ` using ${uniqueFields.join(', ')} as unique identifiers`;
if (result) {
if (result.newCount > 0) {
message += `, ${result.newCount} new objects added`;
}
if (result.updatedCount > 0) {
message += `, ${result.updatedCount} objects replaced`;
}
}
}

toast.success(message);

Dynamic messages that inform users about:

  • Number of objects loaded
  • Which fields were used as unique identifiers
  • How many objects were new vs. updated (when using unique fields)

Error Handling

The component implements comprehensive error handling:

const [error, setError] = useState<string | null>(null);

// Clear errors before new operations
setError(null);

// Display errors both in toast and component state
if (!parsedData) {
setError('Invalid JSON format. Please provide a valid JSON file.');
toast.error('Invalid JSON format. Please provide a valid JSON file.');
return;
}

Dual error display:

  • Toast notifications - Temporary, non-intrusive error messages
  • Component state - Persistent error display within the component

SearchBar Component

The SearchBar component provides powerful search functionality with history management and saved searches.

Search Features

const [inputValue, setInputValue] = useState(searchTerm);

const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setInputValue(value); // Update input immediately
};

const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
handleSearch(inputValue); // Search only on Enter
}
};

Responsive input design:

  • Immediate visual feedback - Input updates as user types
  • Deliberate search execution - Search only triggers on Enter key
  • Prevents excessive filtering - Avoids performance issues with large datasets

Regex Support

The search automatically supports regular expressions:

  • Valid regex patterns - Used directly for advanced pattern matching
  • Invalid regex - Automatically escaped and treated as literal strings
  • Case insensitive - All searches use the 'i' flag

Search History Management

Automatic History Tracking

const [searchHistory, setSearchHistory] = useState<string[]>(() => {
const saved = localStorage.getItem('searchHistory');
return saved ? JSON.parse(saved) : [];
});

const addToHistory = (term: string) => {
if (term.trim() && !searchHistory.includes(term)) {
setSearchHistory(prev => [term, ...prev.slice(0, MAX_HISTORY_ITEMS - 1)]);
}
};

Smart history management:

  • Persistent storage - Uses localStorage to survive browser sessions
  • Duplicate prevention - Doesn't add terms that already exist
  • Limited size - Keeps only the 5 most recent searches
  • Automatic ordering - Most recent searches appear first

Search History UI

{showHistory && searchHistory.length > 0 && (
<div className="absolute top-full left-0 right-0 mt-1 bg-white dark:bg-gray-800 border rounded-md shadow-lg z-50">
<div className="p-2">
<div className="flex items-center justify-between mb-2">
<span className="text-sm font-medium">Recent Searches</span>
<button onClick={clearHistory} className="text-xs text-red-500">
Clear History
</button>
</div>
{searchHistory.map((term, index) => (
<button
key={index}
onClick={() => handleHistoryClick(term)}
className="w-full text-left px-3 py-2 text-sm hover:bg-gray-100 rounded-md"
>
{term}
</button>
))}
</div>
</div>
)}

Dropdown interface with:

  • Click to reuse - Clicking a history item performs that search
  • Clear option - Button to clear entire history
  • Hover effects - Visual feedback for interactive elements

Saved Searches

Bookmark Functionality

const saveSearch = (term: string) => {
if (term.trim() && !savedSearches.includes(term)) {
setSavedSearches(prev => [...prev, term]);
}
};

const removeSavedSearch = (term: string) => {
setSavedSearches(prev => prev.filter(t => t !== term));
};

Persistent bookmarks for frequently used searches:

  • Manual saving - Users explicitly save searches they want to keep
  • Persistent storage - Saved searches survive browser sessions
  • Easy removal - Individual saved searches can be deleted

Saved Search UI

{savedSearches.length > 0 && (
<div className="flex flex-wrap gap-2 mb-4">
{savedSearches.map((term, index) => (
<div key={index} className="flex items-center bg-blue-100 dark:bg-blue-900 rounded-full">
<button
onClick={() => handleHistoryClick(term)}
className="px-3 py-1 text-sm text-blue-800 dark:text-blue-200"
>
{term}
</button>
<button
onClick={() => removeSavedSearch(term)}
className="p-1 text-blue-800 dark:text-blue-200 hover:bg-blue-200 rounded-full mr-1"
>
<BookmarkX className="h-4 w-4" />
</button>
</div>
))}
</div>
)}

Tag-like interface displaying saved searches as removable chips above the search input.

Results Display

{searchTerm && (
<div className="text-sm text-gray-600 dark:text-gray-400">
Found {data.length} {data.length === 1 ? 'result' : 'results'}
</div>
)}

Live result count that updates automatically as the filtered data changes.

Controls Component

The Controls component provides view switching, bulk operations, and application configuration.

View Mode Toggle

const viewModeButtons = [
{ mode: 'card' as ViewMode, icon: LayoutGrid, label: 'Card View' },
{ mode: 'table' as ViewMode, icon: TableIcon, label: 'Table View' }
];

{viewModeButtons.map(({ mode, icon: Icon, label }) => (
<button
key={mode}
onClick={() => setViewMode(mode)}
className={`p-2 rounded-md transition-colors ${
viewMode === mode
? 'bg-blue-600 text-white'
: 'bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300'
}`}
title={label}
>
<Icon className="h-5 w-5" />
</button>
))}

Visual toggle buttons with:

  • Active state styling - Currently selected view is highlighted
  • Icon representation - Grid icon for cards, table icon for table view
  • Tooltips - Hover text explains each option

Bulk Operations

The component handles operations on multiple selected items:

{selectedIds.length > 0 && (
<div className="flex items-center gap-2">
<span className="text-sm text-gray-600 dark:text-gray-400">
{selectedIds.length} selected
</span>
<button
onClick={() => {
deleteObjects(selectedIds);
clearSelection();
}}
className="p-2 rounded-md bg-red-600 text-white hover:bg-red-700"
title="Delete selected items"
>
<Trash2 className="h-5 w-5" />
</button>
</div>
)}

Selection-aware interface that:

  • Shows selection count - Displays number of selected items
  • Provides bulk actions - Delete button for removing multiple items
  • Clears selection - Automatically deselects items after operations

Design Patterns

Consistent State Management

All components use the same patterns for accessing application state:

const { 
searchTerm,
setSearchTerm,
data,
loading
} = useData();

Centralized state access through the custom useData hook.

Error Handling Strategy

Components implement a dual error handling approach:

  1. User-facing errors - Toast notifications for immediate feedback
  2. Component errors - Local error state for persistent display
  3. Console logging - Detailed error information for debugging

Loading State Management

disabled={loading}

Consistent loading states where interactive elements are disabled during async operations.

Dark Mode Support

All components include dark mode styling:

className="bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100"

Automatic theme switching based on the application's dark mode state.

Accessibility Features

  • Keyboard navigation - Enter key support, tab order
  • Screen reader support - Proper ARIA labels and semantic HTML
  • Visual feedback - Hover states, focus indicators
  • Error communication - Clear error messages and validation feedback

These components work together to create a cohesive, user-friendly interface for JSON data visualization and management.