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.