diff --git a/.claude/commands/code-review.md b/.claude/commands/code-review.md new file mode 100644 index 00000000..6f4aedac --- /dev/null +++ b/.claude/commands/code-review.md @@ -0,0 +1,259 @@ +--- +allowed-tools: Bash, Read, Grep, Glob +description: Review uncommitted changes following WordPress PHP, React, and CSS best practices +argument-hint: "[--staged]" +author: Daniel Mejta +--- + +# Code Review Command + +Performs comprehensive code review of uncommitted changes following best practices for WordPress PHP development, React components, and CSS styles. Outputs actionable recommendations with severity ratings and an overall quality score. + +## Usage + +Review all uncommitted changes: +``` +/code-review +``` + +Review only staged changes: +``` +/code-review --staged +``` + +## Review Criteria + +### WordPress PHP Best Practices +- **WordPress Coding Standards (WPCS)**: Spacing, indentation, naming conventions +- **Security**: Proper data escaping (esc_html, esc_attr, esc_url, wp_kses), nonce verification, capability checks +- **Performance**: Efficient database queries, proper caching, avoiding n+1 queries +- **Hooks**: Proper use of actions and filters, appropriate priorities +- **Internationalization**: Proper use of translation functions with correct text domain +- **Error Handling**: Proper exception handling, WP_Error usage +- **Documentation**: PHPDoc blocks for classes, methods, and complex functions +- **Namespace**: Proper PSR-4 autoloading and namespace usage +- **Sanitization**: Proper input sanitization and validation + +### React Best Practices +- **Component Structure**: Proper component composition, single responsibility +- **Hooks**: Correct use of useState, useEffect, useCallback, useMemo +- **Props**: Clear prop types, proper destructuring, avoiding prop drilling +- **Performance**: Unnecessary re-renders, missing dependency arrays, key props +- **Accessibility**: ARIA attributes, semantic HTML, keyboard navigation +- **State Management**: Appropriate state location, avoiding derived state +- **Error Boundaries**: Proper error handling in components +- **Code Organization**: Clear naming, consistent patterns, DRY principles + +### CSS Best Practices +- **Naming**: BEM methodology or consistent naming convention +- **Specificity**: Avoiding overly specific selectors, !important usage +- **Organization**: Logical grouping, consistent ordering of properties +- **Responsiveness**: Mobile-first approach, appropriate breakpoints +- **Accessibility**: Focus states, color contrast, screen reader compatibility +- **Performance**: Efficient selectors, avoiding layout thrashing +- **Modern CSS**: Using CSS custom properties, grid, flexbox appropriately +- **Browser Compatibility**: Considering vendor prefixes and fallbacks + +## Instructions + +Follow these steps to perform the code review: + +### 1. Identify Changed Files + +Use Bash to get the list of uncommitted changes: +```bash +# For all changes (default) +git status --short + +# For staged changes only +git diff --cached --name-only +``` + +If no changes are found, inform the user and exit. + +### 2. Get File Contents and Changes + +For each changed file: +- Use `git diff` (or `git diff --cached` for staged) to see the actual changes +- Use Read tool to get full file context if needed +- Focus review on the changed lines and their surrounding context + +### 3. Analyze by File Type + +**For PHP files** (*.php): +- Check WordPress Coding Standards compliance +- Look for security vulnerabilities (unescaped output, SQL injection, XSS) +- Verify proper sanitization and validation +- Check for proper internationalization +- Review hook usage and priorities +- Verify PHPDoc documentation +- Check namespace and class structure + +**For JavaScript/JSX files** (*.js, *.jsx): +- Review React component structure and patterns +- Check hooks usage and dependencies +- Look for performance issues +- Verify accessibility attributes +- Check for proper error handling +- Review state management patterns +- Verify consistent code style + +**For CSS/SCSS files** (*.css, *.scss): +- Review naming conventions +- Check selector specificity +- Verify responsive design approach +- Check accessibility (focus states, contrast) +- Review property organization +- Look for performance anti-patterns +- Check for modern CSS usage + +### 4. Categorize Issues by Severity + +**CRITICAL** (Score impact: -3 points each) +- Security vulnerabilities (XSS, SQL injection, CSRF) +- Data loss potential +- Breaking changes without fallbacks +- Accessibility violations preventing usage + +**HIGH** (Score impact: -2 points each) +- Performance issues causing significant slowdowns +- Missing error handling in critical paths +- WordPress Coding Standards violations affecting functionality +- Improper hook priorities causing conflicts +- Missing or incorrect internationalization + +**MEDIUM** (Score impact: -1 point each) +- Code style inconsistencies +- Missing documentation +- Non-optimal performance patterns +- Minor accessibility improvements +- Code organization issues + +**LOW** (Score impact: -0.5 points each) +- Minor style suggestions +- Code readability improvements +- Non-critical refactoring opportunities +- Documentation enhancements + +### 5. Calculate Overall Score + +Start with a base score of 10: +- Subtract points based on severity (see above) +- Minimum score is 1 +- Round to nearest 0.5 + +**Score Interpretation**: +- 9.0-10.0: Excellent - Production ready +- 7.0-8.5: Good - Minor improvements recommended +- 5.0-6.5: Fair - Several issues to address +- 3.0-4.5: Needs Work - Significant issues present +- 1.0-2.5: Critical - Major issues must be fixed + +### 6. Format Output + +Present the review in this structure: + +``` +# Code Review Results + +## Overview +- Files reviewed: [count] +- Total issues: [count] +- Overall score: [X.X/10] - [interpretation] + +## Issues by Severity + +### CRITICAL (count) +1. [File:Line] - [Issue description] + Recommendation: [Specific fix] + +### HIGH (count) +... + +### MEDIUM (count) +... + +### LOW (count) +... + +## Summary +[Brief summary of main concerns and positive aspects] + +## Next Steps +[Prioritized action items] +``` + +## Error Handling + +- If git is not available, inform the user +- If no changes detected, confirm with user that working tree is clean +- If a file cannot be read, note it and continue with other files +- If diff is too large, focus on changed sections rather than full file analysis +- If encountering binary files, skip with a note + +## Performance Considerations + +- For large diffs (>1000 lines), provide a warning and option to continue +- Focus on changed lines and immediate context (±5 lines) +- Use Grep for pattern matching rather than reading entire large files +- Process files in parallel when checking for common patterns + +## Examples + +### Example Output + +``` +# Code Review Results + +## Overview +- Files reviewed: 5 +- Total issues: 8 +- Overall score: 7.5/10 - Good + +## Issues by Severity + +### CRITICAL (1) +1. src/Admin.php:45 - Unescaped output in admin interface + Recommendation: Use esc_html() when outputting $user_name to prevent XSS + +### HIGH (2) +1. assets/components/Field.js:23 - Missing dependency in useEffect + Recommendation: Add 'value' to dependency array to prevent stale closures + +2. src/Integration.php:78 - Direct database query without prepare() + Recommendation: Use $wpdb->prepare() to prevent SQL injection + +### MEDIUM (3) +1. assets/styles/field.scss:15 - Using px instead of rem for font-size + Recommendation: Use rem units for better accessibility + +2. src/Helper.php:102 - Missing PHPDoc block + Recommendation: Add @param and @return documentation + +3. assets/components/Label.js:10 - Prop types not defined + Recommendation: Add PropTypes or use TypeScript + +### LOW (2) +1. assets/styles/layout.scss:45 - Consider using CSS custom properties + Recommendation: Replace hardcoded colors with CSS variables + +2. src/Options.php:200 - Consider extracting method for better readability + Recommendation: Extract validation logic into separate method + +## Summary +The code is generally well-structured and follows most best practices. The critical XSS vulnerability should be addressed immediately. React hooks usage needs attention to prevent subtle bugs. CSS could benefit from more modern patterns. + +## Next Steps +1. Fix XSS vulnerability in Admin.php (CRITICAL) +2. Add missing useEffect dependency (HIGH) +3. Refactor database query to use prepare() (HIGH) +4. Address remaining medium and low priority items in subsequent iterations +``` + +## Notes + +- This command analyzes code quality but does not make changes +- For automatic fixes, consider using separate commands like /phpcbf for PHP +- Review is opinionated based on WordPress and React best practices +- Scores are relative to the codebase being reviewed, not absolute measures +- Consider running phpcs and npm lint commands for automated checks first diff --git a/CHANGELOG.md b/CHANGELOG.md index a71e728c..a042b71d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [4.5.0] - 2026-02-17 + +### Added +- `collapse` prop for MultiGroup field to control item collapsibility +- `wrapper` field type for visual grouping of fields without value nesting +- `columns` field type for multi-column grid layout +- `FieldFactory` class for programmatic field creation +- `setTitle` support for all field components + +### Changed +- Removed React portals and render fields inside a single app container +- Switched field layout to CSS grid +- Cleaned up CSS/SCSS styles and React classnames +- Wrapped root fields in container div for layout targeting +- Applied flex layout to app instance fields wrapper +- Rewrote README with comprehensive field types, examples, and integration reference +- Standardized all 57 field type docs with consistent template + +### Fixed +- Active tab now preserved after saving options page +- Grid layout for fields without label text +- Explicit props now properly override spread props in Label and FieldComponent + ## [4.4.2] - 2026-02-05 ### Fixed diff --git a/CLAUDE.md b/CLAUDE.md index 46992395..268dc1fd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,152 +1,151 @@ # CLAUDE.md -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Build/Test Commands -- Start dev server: `npm run start` -- Build for production: `npm run build` -- Analyze bundle: `npm run build:analyze` -- PHP code standards check: `composer run phpcs` -- PHP code beautifier: `composer run phpcbf` - -## Code Style Guidelines -- PHP: WordPress Coding Standards (WPCS) with customizations in phpcs.xml -- PHP version: 8.1+ -- WordPress version: 6.2+ -- JS: Use WordPress scripts standards -- Prefix PHP globals with `wpifycf` -- Translation text domain: `wpify-custom-fields` -- React components use PascalCase -- JS helpers use camelCase -- Namespace: `Wpify\CustomFields` -- PHP class files match class name (PSR-4) -- Import paths: Use `@` alias for assets directory in JS -- Error handling: Use custom exceptions in `Exceptions` directory -- Documentation is in PHPDoc format and in docs folder in md format -- When generating PHP code, always use WordPress Coding Standards - -## Extending Field Types -To create a custom field type, the following components are required: - -1. **PHP Filters**: - - `wpifycf_sanitize_{type}` - For sanitizing field values - - `wpifycf_wp_type_{type}` - To specify WordPress data type (integer, number, boolean, object, array, string) - - `wpifycf_default_value_{type}` - To define default values - -2. **JavaScript Components**: - - Create a React component for the field - - Add validation method to the component (`YourComponent.checkValidity`) - - Register the field via `addFilter('wpifycf_field_{type}', 'wpify_custom_fields', () => YourComponent)` - -3. **Multi-field Types**: - - Custom field types can have multi-versions by prefixing with `multi_` - - Leverage the existing `MultiField` component for implementation - - Use `checkValidityMultiFieldType` helper for validation - -4. **Field Component Structure**: - - Field components receive props like `id`, `htmlId`, `onChange`, `value`, etc. - - CSS classes should follow pattern: `wpifycf-field-{type}` - - Return JSX with appropriate HTML elements - -## Documentation Standards -When writing or updating documentation: - -### PHP Code Examples -- Use tabs for indentation, not spaces -- Follow WordPress Coding Standards for all PHP examples: - - Add spaces inside parentheses for conditions: `if ( ! empty( $var ) )` - - Add spaces after control structure keywords: `if (...) {` - - Add spaces around logical operators: `$a && $b`, `! $condition` - - Add spaces around string concatenation: `$a . ' ' . $b` - - Add spaces for function parameters: `function_name( $param1, $param2 )` - - Use proper array formatting with tabs for indentation: - ```php - array( - 'key1' => 'value1', - 'key2' => 'value2', - ) - ``` - - Maintain consistent spacing around array arrow operators: `'key' => 'value'` - - Use spaces in associative array access: `$array[ 'key' ]` - -### Documentation Structure for Field Types -Field type documentation should follow this consistent structure: -1. **Title and Description** - Clear explanation of the field's purpose -2. **Field Type Definition** - Example code following WordPress coding standards -3. **Properties Section**: - - Default field properties - - Specific properties unique to the field type -4. **Stored Value** - Explanation of how data is stored in the database -5. **Example Usage** - Real-world examples with WordPress coding standards -6. **Notes** - Important details about the field's behavior and uses - -### Documentation Structure for Integrations -Integration documentation should follow this consistent structure: -1. **Title and Overview** - Clear explanation of the integration's purpose -2. **Requirements** - Any specific plugins or dependencies required (if applicable) -3. **Usage Example** - PHP code example following WordPress coding standards -4. **Parameters Section**: - - Required parameters with descriptions - - Optional parameters with descriptions and default values -5. **Data Storage** - How and where the integration stores its data -6. **Retrieving Data** - How to access stored data programmatically -7. **Advanced Usage** - Examples of tabs, conditional display, etc. (as applicable) - -### Security in Examples -- Always include proper data escaping in examples: - - `esc_html()` for plain text output - - `esc_attr()` for HTML attributes - - `esc_url()` for URLs - - `wp_kses()` for allowing specific HTML - -### Consistency -- Maintain consistent terminology across all documentation files -- Use consistent formatting for property descriptions -- Keep parameter documentation format consistent: `name` _(type)_ - description -- When documenting integrations, use consistent parameter naming and structure - -### Integration-Specific Notes -- For WooCommerce integrations, always mention compatibility with HPOS when applicable -- Product Options integrations should list common tab IDs from WooCommerce -- Order and Subscription integrations should include examples of retrieving meta -- All integration documentation should include examples of tabs and conditional display -- When documenting options pages, always include proper menu/page configuration - -### File Organization -- Field type documentation goes in `docs/field-types/` -- Integration documentation goes in `docs/integrations/` -- Feature documentation goes in `docs/features/` -- All documentation files should use `.md` extension -- Main index files (integrations.md, field-types.md) should link to all related docs - -## Conditional Fields -The plugin provides a robust conditional logic system for dynamically showing/hiding fields: - -### Condition Structure -Each condition requires: -- `field`: The ID of the field to check (can use path references) -- `condition`: The comparison operator -- `value`: The value to compare against - -### Available Operators -- `==`: Equal (default) -- `!=`: Not equal -- `>`, `>=`, `<`, `<=`: Comparison operators -- `between`: Value is between two numbers, inclusive -- `contains`, `not_contains`: String contains/doesn't contain value -- `in`, `not_in`: Value is/isn't in an array -- `empty`, `not_empty`: Value is/isn't empty - -### Multiple Conditions -- Combine with `'and'` (default) or `'or'` between conditions -- Create nested condition groups with sub-arrays for complex logic - -### Path References -- Dot notation for nested fields: `parent.child` -- Hash symbols for relative paths: `#` (parent), `##` (grandparent) -- Array access: `multi_field[0]` for specific items - -### Technical Implementation -- Conditional logic lives in `Field.js`, `hooks.js` (useConditions), and `functions.js` -- Hidden fields are still submitted but have `data-hide-field="true"` attribute -- Conditions are evaluated in real-time as users interact with the form +## Project Overview + +WPify Custom Fields — WordPress plugin providing 58 field types across 15 integration points (metaboxes, options pages, taxonomy terms, users, WooCommerce products/orders/coupons, Gutenberg blocks, etc.). PHP 8.1+ / WordPress 6.2+ / React 18. + +Entry point: `custom-fields.php` → singleton `wpify_custom_fields()` returns `CustomFields` instance. + +## Directory Structure + +``` +src/ # PHP source (PSR-4: Wpify\CustomFields) + CustomFields.php # Main class — factory methods, sanitization, type mapping + Api.php # REST API endpoints (/wpifycf/v1/) + FieldFactory.php # Fluent PHP API for building field definitions + Helpers.php # URL fetching, post/term queries, file operations + Fields/ # PHP field type handlers (DirectFileField) + Integrations/ # All 15 integration classes + Exceptions/ # MissingArgumentException, CustomFieldsException +assets/ # JS/React source (@ alias in imports) + custom-fields.js # Entry point — bootstraps React apps from DOM containers + components/ # Shared: App, Field, AppContext, MultiField, GutenbergBlock, Tabs, Label, etc. + fields/ # 59 field type React components + helpers/ # hooks.js, functions.js, validators.js, field-types.js, generators.js + styles/ # SCSS with CSS custom properties and container queries +docs/ # Markdown documentation (field-types/, integrations/, features/) +build/ # Webpack output (generated) +``` + +## Build Commands + +- Dev server: `npm run start` +- Production build: `npm run build` +- Bundle analysis: `npm run build:analyze` +- PHP code standards: `composer run phpcs` +- PHP auto-fix: `composer run phpcbf` + +## Architecture + +### PHP Integration Class Hierarchy + +``` +BaseIntegration (abstract) +│ normalize_items(), enqueue(), register_rest_options() +│ +├── OptionsIntegration (abstract) +│ │ print_app(), prepare_items_for_js(), get/set_field(), set_fields_from_post_request() +│ │ +│ ├── Options — Admin menu pages (get_option / update_option) +│ ├── SiteOptions — Multisite network options +│ ├── WooCommerceSettings — WC settings tabs +│ │ +│ └── ItemsIntegration (abstract) +│ │ Adds get_item_id() for item-bound storage +│ │ +│ ├── Metabox — Post meta boxes +│ ├── Taxonomy — Term meta fields +│ ├── User — User profile fields +│ ├── Comment — Comment meta +│ ├── MenuItem — Nav menu item meta +│ ├── ProductOptions — WC product data tabs +│ ├── ProductVariationOptions — WC product variations +│ ├── CouponOptions — WC coupon fields +│ ├── OrderMetabox — WC orders (HPOS compatible) +│ ├── SubscriptionMetabox — WC Subscriptions +│ └── WcMembershipPlanOptions — WC Memberships +│ +└── GutenbergBlock — Block attributes, server-side rendering, InnerBlocks +``` + +### Data Flow: PHP → JavaScript + +1. **Normalize** — `normalize_items()` adds IDs, global_ids, resolves type aliases, registers async option endpoints +2. **Prepare** — `prepare_items_for_js()` builds input names, fetches current values from DB +3. **Render** — `print_app()` outputs a `.wpifycf-app-instance` container with all field data in `data-fields` JSON attribute +4. **Bootstrap** — JS entry reads `data-fields`, creates React root with `AppContextProvider` +5. **Render tree** — `App` → `RootFields` → `Field` (dispatcher) → specific field component +6. **Submit** — Hidden `` elements carry values; PHP `set_fields_from_post_request()` sanitizes and saves +7. **Gutenberg** — Uses controlled state (`attributes` / `setAttributes`) instead of form submission + +### Key JS Components + +- **`Field.js`** — Central dispatcher: resolves type to component via `getFieldComponentByType()`, evaluates conditions (`useConditions`), runs validation (`checkValidity`), handles generators +- **`AppContext.js`** — Global state provider: values, fields, tabs, config. Supports both controlled (Gutenberg) and uncontrolled (form) modes +- **`MultiField.js`** — Generic repeater: add/remove/reorder (Sortable.js), min/max constraints. All `multi_*` types are thin wrappers around this +- **`GutenbergBlock.js`** — View/Edit mode toggle, server-side block rendering, InnerBlocks via HTML comment replacement +- **`functions.js`** — `evaluateConditions()`, `getValueByPath()` (dot notation + relative `#`/`##` paths), `interpolateFieldValues()` +- **`validators.js`** — `checkValidityStringType`, `checkValidityNumberType`, `checkValidityGroupType`, `checkValidityMultiFieldType()` factory +- **`hooks.js`** — `useConditions`, `useMulti`, `usePosts`, `useTerms`, `useOptions`, `useMediaLibrary`, `useValidity`, `useSortableList` + +## Code Style + +- **PHP**: WordPress Coding Standards (WPCS) — see `phpcs.xml` for project customizations +- **JS**: WordPress scripts standards via `@wordpress/scripts` +- **CSS**: SCSS, BEM-style with `wpifycf-` prefix, CSS custom properties (`--wpifycf-*`), container queries on `.wpifycf-app-instance` and `.wpifycf-field__control` +- **Naming**: PHP namespace `Wpify\CustomFields` (PSR-4), React components PascalCase, JS helpers camelCase +- **Prefix**: PHP globals `wpifycf`, text domain `wpify-custom-fields` +- **JS imports**: `@` alias = `assets/` directory +- **Docs**: PHPDoc in code, markdown in `docs/`. Follow WordPress Coding Standards in PHP examples (tabs, spaces in parentheses/functions). Always escape output: `esc_html()`, `esc_attr()`, `esc_url()`, `wp_kses()` + +## Key Patterns + +### Integration Lifecycle +- **Render**: `normalize_items()` → `prepare_items_for_js()` → `print_app(context, tabs, attrs, items)` +- **Save**: `normalize_items()` → `set_fields_from_post_request(items)` +- **Register meta**: `normalize_items()` → `flatten_items()` → `register_{type}_meta()` per field + +### Field Type Aliases (backward compatibility) +`switch` → `toggle`, `multiswitch` → `multi_toggle`, `multiselect` → `multi_select`, `colorpicker` → `color`, `gallery` → `multi_attachment`, `repeater` → `multi_group` + +### PHP Extensibility Filters +- `wpifycf_sanitize_{type}` — custom sanitization +- `wpifycf_wp_type_{type}` — WordPress data type mapping (integer, number, boolean, object, array, string) +- `wpifycf_default_value_{type}` — default values +- `wpifycf_items` — filter normalized items + +### JS Extensibility Filters (@wordpress/hooks) +- `wpifycf_field_{type}` — register custom field component +- `wpifycf_definition` — filter field definitions before render +- `wpifycf_generator_{name}` — auto-generate field values (e.g., UUID) + +### Extending Field Types +- **PHP**: Register `wpifycf_sanitize_{type}`, `wpifycf_wp_type_{type}`, `wpifycf_default_value_{type}` filters +- **JS**: Create component in `assets/fields/`, add static `checkValidity(value, field)` method, register with `addFilter('wpifycf_field_{type}', ...)` +- **Multi-version**: Wrap with `MultiField`, use `checkValidityMultiFieldType(type)` helper +- Full guide: `docs/features/extending.md` + +### Conditional Logic +Conditions array with operators (`==`, `!=`, `>`, `>=`, `<`, `<=`, `between`, `contains`, `not_contains`, `in`, `not_in`, `empty`, `not_empty`), `and`/`or` combinators, nested groups, relative path refs (`#` parent, `##` grandparent). Hidden fields still submit values with `data-hide-field="true"`. Full docs: `docs/features/conditions.md` + +### Validation +Field components export static `checkValidity(value, field)` → array of error strings. Form submission blocked if errors. Validators in `assets/helpers/validators.js`. Full docs: `docs/features/validation.md` + +## Documentation + +When writing or updating docs in `docs/`: +- Follow existing templates — consistent structure for field types, integrations, and features (see any existing file as reference) +- PHP examples: WordPress Coding Standards with tabs, spaces in parentheses/functions +- Always escape output in examples (`esc_html()`, `esc_attr()`, `esc_url()`, `wp_kses()`) +- Parameter format: `name` _(type)_ — description +- File organization: `docs/field-types/`, `docs/integrations/`, `docs/features/` + +## Self-Maintenance + +When your changes invalidate or create gaps in this file, update it as part of the same task. Typical triggers: +- Build commands or scripts change +- New integrations, field types, or major features are added +- Class hierarchy or data flow changes +- File/directory structure changes + +Keep updates minimal, match existing style, do not add session-specific or speculative content. diff --git a/assets/components/App.js b/assets/components/App.js index c9b71504..6ac8981a 100644 --- a/assets/components/App.js +++ b/assets/components/App.js @@ -10,10 +10,8 @@ export function App ({ form }) { const { validity, validate, handleValidityChange } = useValidity({ form }); const renderOptions = useMemo(() => ({ - noFieldWrapper: ['options', 'edit_term', 'add_term'].includes(context), - noControlWrapper: false, isRoot: true, - }), [context]); + }), []); const filteredFields = applyFilters('wpifycf_definition', fields, values, { context }); diff --git a/assets/components/ControlWrapper.js b/assets/components/ControlWrapper.js index dc0006bd..6db09066 100644 --- a/assets/components/ControlWrapper.js +++ b/assets/components/ControlWrapper.js @@ -1,21 +1,8 @@ -import { useContext } from 'react'; -import { AppContext } from '@/components/AppContext';; - export function ControlWrapper ({ renderOptions = {}, children }) { - const { context } = useContext(AppContext); - if (renderOptions.noControlWrapper) { return children; } - if (context === 'edit_term' && renderOptions.isRoot) { - return ( -