Plugin Usage Examples
Examples of useful plugins for enhancing React JSONR
1. Internationalization Plugin
This plugin translates text content using a translation dictionary:
// Translation dictionary
const translations = {
en: {
'welcome': 'Welcome to our app',
'description': 'This app uses React JSONR to render UI from JSON',
'submit': 'Submit',
'cancel': 'Cancel'
},
es: {
'welcome': 'Bienvenido a nuestra aplicación',
'description': 'Esta aplicación utiliza React JSONR para renderizar UI desde JSON',
'submit': 'Enviar',
'cancel': 'Cancelar'
},
fr: {
'welcome': 'Bienvenue dans notre application',
'description': 'Cette application utilise React JSONR pour afficher l\'interface à partir de JSON',
'submit': 'Soumettre',
'cancel': 'Annuler'
}
};
// A plugin that translates text based on language code
const TranslationPlugin = (language = 'en') => ({
enter(node, context) {
const langDict = translations[language] || translations.en;
// Handle direct text props
if (node.props?.text && typeof node.props.text === 'string') {
const key = node.props.text;
if (langDict[key]) {
node.props.text = langDict[key];
}
}
// Handle children text
if (node.props?.children && typeof node.props.children === 'string') {
const key = node.props.children;
if (langDict[key]) {
node.props.children = langDict[key];
}
}
// Handle label props for buttons
if (node.type === 'Button' && node.props?.label) {
const key = node.props.label;
if (langDict[key]) {
node.props.label = langDict[key];
}
}
}
});
// Usage
const transformed = await transformJsonTree(jsonUI, [
TranslationPlugin('es') // Use Spanish translations
]);
2. Theme Injection Plugin
This plugin adds theme-based styling to components:
// Theme definitions
const themes = {
light: {
button: {
backgroundColor: '#3b82f6',
color: 'white',
borderRadius: '4px'
},
input: {
borderColor: '#d1d5db',
backgroundColor: 'white',
borderRadius: '4px'
},
form: {
backgroundColor: '#f9fafb',
padding: '1rem',
borderRadius: '8px'
}
},
dark: {
button: {
backgroundColor: '#1f2937',
color: 'white',
borderRadius: '4px'
},
input: {
borderColor: '#4b5563',
backgroundColor: '#111827',
color: 'white',
borderRadius: '4px'
},
form: {
backgroundColor: '#1f2937',
padding: '1rem',
borderRadius: '8px',
color: 'white'
}
}
};
// Theme injection plugin
const ThemePlugin = (themeName = 'light') => ({
enter(node, context) {
const theme = themes[themeName] || themes.light;
// No props? Create them
if (!node.props) {
node.props = {};
}
// No style? Create it
if (!node.props.style) {
node.props.style = {};
}
// Apply theme based on component type
switch(node.type) {
case 'Button':
node.props.style = { ...node.props.style, ...theme.button };
break;
case 'Input':
node.props.style = { ...node.props.style, ...theme.input };
break;
case 'Form':
node.props.style = { ...node.props.style, ...theme.form };
break;
}
}
});
// Usage
const transformed = await transformJsonTree(jsonUI, [
ThemePlugin('dark') // Use dark theme
]);
3. Analytics Tagging Plugin
This plugin adds data attributes for analytics tracking:
const AnalyticsPlugin = (pageSection = 'main') => ({
enter(node, context) {
if (!node.props) {
node.props = {};
}
// Add analytics data attributes
node.props['data-analytics'] = 'true';
node.props['data-section'] = pageSection;
// Add specific tracking for interactive elements
if (node.props.onClick) {
node.props['data-track'] = 'click';
node.props['data-element-type'] = node.type;
// Generate an ID if none exists
if (!node.props.id) {
node.props.id = `${pageSection}-${node.type}-${context.index}`;
}
// Add the ID as a tracking property
node.props['data-element-id'] = node.props.id;
}
// Add form tracking
if (node.type === 'Form') {
node.props['data-track'] = 'form-interaction';
node.props['data-form-name'] = node.props.title || 'unknown-form';
}
}
});
// Usage
const transformed = await transformJsonTree(jsonUI, [
AnalyticsPlugin('checkout') // Add checkout section analytics
]);
4. Validation Plugin
This plugin adds validation to form elements:
const ValidationPlugin = {
enter(node, context) {
if (!node.props) {
node.props = {};
}
// Add validation rules to input fields
if (node.type === 'Input') {
const { name, type } = node.props;
// Email validation
if (type === 'email') {
node.props.pattern = '[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,}$';
node.props['data-error-message'] = 'Please enter a valid email address';
}
// Required fields
if (node.props.required) {
node.props['aria-required'] = 'true';
if (!node.props['data-error-message']) {
node.props['data-error-message'] = `${node.props.label} is required`;
}
}
// Password validation
if (type === 'password') {
node.props.minLength = 8;
node.props['data-error-message'] = 'Password must be at least 8 characters';
}
}
}
};
// Usage
const transformed = await transformJsonTree(jsonUI, [ValidationPlugin]);
Combining Multiple Plugins
Define your plugins, each with a focused purpose
Arrange them in the desired order (plugins run in sequence)
Apply them all at once in the transformation pipeline
// Example of combining multiple plugins
const transformed = await transformJsonTree(jsonUI, [
AuthorizationPlugin(user), // First, filter out elements the user can't see
TranslationPlugin(userLanguage), // Then, translate the remaining text
ValidationPlugin, // Add form validation
ThemePlugin(userTheme), // Apply theme styling
AnalyticsPlugin(currentSection) // Finally, add analytics tracking
]);
// Then render as usual
const element = renderNode(transformed, registry, { eventHandlers });
Plugins are applied in sequence, so order matters. Each plugin sees the changes made by previous plugins.
Creating Your Own Plugins
To create your own plugin, implement the TransformVisitor
interface:
// Basic plugin template
const MyPlugin = {
// Called when a node is first visited (before processing children)
enter(node, context) {
// Modify the node here
console.log(`Visiting ${node.type} at depth ${context.depth}`);
// Skip children if needed
// context.skipChildren();
},
// Called after all children have been processed (optional)
exit(node, context) {
// Do post-processing here
console.log(`Finished processing ${node.type}`);
}
};
For async operations, make your methods async:
const AsyncPlugin = {
async enter(node, context) {
// Fetch data, process asynchronously, etc.
const data = await fetchSomeData();
node.props.data = data;
}
};