Skip to content

Expose reusable settings UI for add-ons#95

Open
kzamanbd wants to merge 12 commits into
developfrom
feat/expose-shared-components
Open

Expose reusable settings UI for add-ons#95
kzamanbd wants to merge 12 commits into
developfrom
feat/expose-shared-components

Conversation

@kzamanbd

@kzamanbd kzamanbd commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Lets add-ons (e.g. Texty Pro) reuse Texty's schema-driven settings UI instead of
duplicating it, using the same webpack dependency-extraction pattern as Dokan
lite → pro. Underpins the Texty Pro SMS Automations PR.

What's included

Component export (webpack library + dependency extraction)

  • New components webpack entry → dist/components.js exposing
    window.texty.components (handle texty-components).
  • webpack-dependency-mapping.js (requestToExternal/requestToHandle) wired to a
    custom DependencyExtractionWebpackPlugin, so @texty/components externalizes to
    the global + script handle.
  • src/components/index.tsx barrel exports NotificationGroupSettings and
    PhoneField.
  • Admin/Menu registers texty-components (depends on texty-admin so it merges
    onto window.texty after the localized data).

Generalized NotificationGroupSettings

  • Optional schemaPath / savePath props + a generic save-fold (collapse any
    <id>_<key> / <id>::<key> children into the per-id payload) → renders any
    feature's { schema, values }, not just built-in notification groups.

Extensibility / cleanup

  • ICON_MAP / LOGO_MAP made filterable via texty_notification_icon_map /
    texty_notification_logo_map.
  • REST query strings built with @wordpress/url addQueryArgs.
  • Includes the earlier gateway-logo/customization filter work on this branch.

Consumer

Add-ons: import { NotificationGroupSettings, PhoneField } from '@texty/components'.

Test

  • npm run build (tsc clean); User Events / Integrations tabs render unchanged;
    the components bundle exposes window.texty.components.

Summary by CodeRabbit

  • New Features

    • Notifications settings and tabs are now more flexible, allowing additional sections and custom configuration to be added more easily.
    • A shared component bundle is now available for extensions and add-ons.
  • Bug Fixes

    • Updated several gateway logos so they load from the correct image locations.
    • Improved notification loading and saving behavior for more reliable setup across different notification groups.

kzamanbd added 7 commits June 29, 2026 15:38
… style)

Expose Texty's schema-driven settings UI so add-ons (Texty Pro) can import it
instead of duplicating the renderer:

- Build a `components` webpack entry from src/components/index.tsx as a window
  library `textyComponents` (handle `texty-components`), and wire a custom
  DependencyExtractionWebpackPlugin via webpack-dependency-mapping.js so
  `@texty/components` externalizes to that global + handle.
- Register the `texty-components` script in Admin/Menu so dependents load it.
- Generalize NotificationGroupSettings: optional schemaPath/savePath props and a
  generic save-fold (collapse any `<id>_<key>` / `<id>::<key>` children into the
  per-id payload), so it renders any feature's {schema, values} — not just the
  built-in notifications groups. Exported as `SchemaSettings`.
Use the nested window.texty.components global (Dokan style) instead of a flat
textyComponents. The library assignment is merge-safe
((window.texty = window.texty || {}).components = …), and texty-components now
depends on texty-admin so it loads after the localized `var texty = {…}` data and
merges onto it rather than being clobbered.
…uery args

- Apply `texty_notification_icon_map` / `texty_notification_logo_map` JS filters
  to the header ICON_MAP / LOGO_MAP so add-ons can register icons/logos for their
  own groups.
- Build REST query strings with @wordpress/url's addQueryArgs instead of manual
  concatenation / encodeURIComponent (schema?group, notifications?context).
@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@kzamanbd, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 47 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 021eebdb-e074-4d6e-acc9-cf74490012da

📥 Commits

Reviewing files that changed from the base of the PR and between d36b30b and 4f8f018.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (3)
  • src/pages/notifications/components/NotificationGroupSettings.tsx
  • src/pages/notifications/index.tsx
  • texty.php
📝 Walkthrough

Walkthrough

Introduces a shared texty-components webpack bundle exposed on window.texty.components for add-on consumption, with matching PHP script registration. Refactors NotificationGroupSettings to accept configurable schema/save endpoints and a schema-driven save algorithm. Makes the notifications tab list filter-extensible via applyFilters. Reorganizes gateway logo assets under assets/images/gateways/.

Changes

Add-on extensibility infrastructure and notification refactoring

Layer / File(s) Summary
Webpack components bundle and dependency mapping
webpack-dependency-mapping.js, webpack.config.js
Adds webpack-dependency-mapping.js with requestToExternal/requestToHandle for @texty/components, extends webpack.config.js with explicit index and components entries (the latter exposed on window.texty.components), and replaces the default DependencyExtractionWebpackPlugin with a configured instance.
PHP registration of texty-components script
includes/Admin/Menu.php
Menu::enqueue_scripts() conditionally registers texty-components from dist/components.js when the asset metadata file exists, merging its dependencies with texty-admin.
Public components barrel for add-ons
src/components/index.tsx
Creates src/components/index.tsx re-exporting NotificationGroupSettings and PhoneField as the public add-on API surface.
Schema-driven and filter-extensible NotificationGroupSettings
src/pages/notifications/components/NotificationGroupSettings.tsx
Adds optional schemaPath/savePath props, computes fetchPath/postPath via addQueryArgs, replaces hard-coded role/recipient save logic with a schema-driven folding algorithm over collapsible_switch fields, adds Zap to ICON_MAP, and wires applyFilters-based overrides for icon/logo maps.
Filter-extensible notifications tab list
src/pages/notifications/index.tsx, src/pages/notifications/hooks/useNotifications.ts
Replaces static tab markup with a dynamically constructed tabs array via applyFilters('texty_notifications_tabs', []), maps over tabs for TabsTrigger/TabsContent rendering, and switches the notifications fetch URL to use addQueryArgs.
Gateway logo path reorganization
includes/Gateways/Clickatell.php, includes/Gateways/Fake.php, includes/Gateways/Plivo.php, includes/Gateways/Twilio.php, includes/Gateways/Vonage.php
Updates logo() return paths in all five gateways to point to the new assets/images/gateways/ subdirectory.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

Dev Review Done

Suggested reviewers

  • mrabbani

Poem

🐇 A components bundle, now shared with add-ons galore,
The notification tabs can be filtered and more!
Gateway logos moved to a tidy new nest,
Schema-driven saving puts old hard-code to rest.
Hop hop hooray, extensibility's here—
The rabbit approves with a wiggle and cheer! 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: exposing reusable settings UI components for add-ons.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/expose-shared-components

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@kzamanbd kzamanbd changed the title Expose reusable settings UI for add-ons (dokan-lite style) Expose reusable settings UI for add-ons Jun 29, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pages/notifications/components/NotificationGroupSettings.tsx`:
- Around line 55-63: The Settings form key is currently derived only from
groupId in NotificationGroupSettings, so custom consumers using
schemaPath/savePath without a groupId can reuse the same instance and leak form
state between endpoints. Update the keying logic in NotificationGroupSettings to
include the resolved endpoint identity from fetchPath and postPath, and keep the
existing groupId-based key only as part of that composite so different
schemas/save targets mount distinct Settings instances.
- Around line 42-49: Make NotificationGroupSettings’ public props contract
mutually exclusive so invalid combinations cannot compile: in
NotificationGroupSettings, replace the current Props shape with a union that
allows either the built-in groupId flow or the custom endpoint flow, and require
schemaPath and savePath together for the custom case. Update the component’s
prop typing and any related call sites/helpers in NotificationGroupSettings so
<NotificationGroupSettings schemaPath="..." /> and bare
<NotificationGroupSettings /> are rejected unless they provide the correct
paired fields.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2855e4f2-edb2-4a6d-8abf-87a340aab108

📥 Commits

Reviewing files that changed from the base of the PR and between ea4c8d6 and d36b30b.

⛔ Files ignored due to path filters (10)
  • assets/images/gateways/clickatell-logo.png is excluded by !**/*.png
  • assets/images/gateways/clickatell.svg is excluded by !**/*.svg
  • assets/images/gateways/common-sms.png is excluded by !**/*.png
  • assets/images/gateways/plivo-logo.png is excluded by !**/*.png
  • assets/images/gateways/plivo.svg is excluded by !**/*.svg
  • assets/images/gateways/twilio-logo.png is excluded by !**/*.png
  • assets/images/gateways/twilio.svg is excluded by !**/*.svg
  • assets/images/gateways/vonage-logo.png is excluded by !**/*.png
  • assets/images/gateways/vonage.svg is excluded by !**/*.svg
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (12)
  • includes/Admin/Menu.php
  • includes/Gateways/Clickatell.php
  • includes/Gateways/Fake.php
  • includes/Gateways/Plivo.php
  • includes/Gateways/Twilio.php
  • includes/Gateways/Vonage.php
  • src/components/index.tsx
  • src/pages/notifications/components/NotificationGroupSettings.tsx
  • src/pages/notifications/hooks/useNotifications.ts
  • src/pages/notifications/index.tsx
  • webpack-dependency-mapping.js
  • webpack.config.js

Comment on lines 42 to 49
type Props = {
groupId: string;
// Built-in notifications group (renders /notifications/schema?group=…).
groupId?: string;
// Or point at any endpoint returning { schema, values } (e.g. Texty Pro's
// Automations), with a matching save endpoint.
schemaPath?: string;
savePath?: string;
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Make the public props contract impossible to misuse.

schemaPath and savePath are optional independently here, so <NotificationGroupSettings schemaPath="/foo" /> still saves to /texty/v1/notifications, and <NotificationGroupSettings /> fetches /texty/v1/notifications/schema?group=. Since src/components/index.tsx now exports this as add-on API, these invalid combinations should be rejected up front instead of failing against the wrong endpoint.

Proposed fix
-type Props = {
-  // Built-in notifications group (renders /notifications/schema?group=…).
-  groupId?: string;
-  // Or point at any endpoint returning { schema, values } (e.g. Texty Pro's
-  // Automations), with a matching save endpoint.
-  schemaPath?: string;
-  savePath?: string;
-};
+type Props =
+  | {
+      // Built-in notifications group (renders /notifications/schema?group=…).
+      groupId: string;
+      schemaPath?: never;
+      savePath?: never;
+    }
+  | {
+      // Custom feature endpoints must be provided as a pair.
+      groupId?: never;
+      schemaPath: string;
+      savePath: string;
+    };

Also applies to: 60-63, 155-157

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/notifications/components/NotificationGroupSettings.tsx` around
lines 42 - 49, Make NotificationGroupSettings’ public props contract mutually
exclusive so invalid combinations cannot compile: in NotificationGroupSettings,
replace the current Props shape with a union that allows either the built-in
groupId flow or the custom endpoint flow, and require schemaPath and savePath
together for the custom case. Update the component’s prop typing and any related
call sites/helpers in NotificationGroupSettings so <NotificationGroupSettings
schemaPath="..." /> and bare <NotificationGroupSettings /> are rejected unless
they provide the correct paired fields.

Comment on lines +55 to +63
const NotificationGroupSettings = ({
groupId,
schemaPath,
savePath,
}: Props) => {
const fetchPath =
schemaPath ??
addQueryArgs('/texty/v1/notifications/schema', { group: groupId ?? '' });
const postPath = savePath ?? '/texty/v1/notifications';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Key the form by endpoint, not only by group ID.

After this was generalized, every custom consumer that passes schemaPath/savePath without a groupId gets key={undefined}. Switching between two custom schemas will reuse the same <Settings> instance and carry its internal dirty/form state across datasets.

Proposed fix
-        key={groupId}
+        key={fetchPath}

Also applies to: 215-216

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/notifications/components/NotificationGroupSettings.tsx` around
lines 55 - 63, The Settings form key is currently derived only from groupId in
NotificationGroupSettings, so custom consumers using schemaPath/savePath without
a groupId can reuse the same instance and leak form state between endpoints.
Update the keying logic in NotificationGroupSettings to include the resolved
endpoint identity from fetchPath and postPath, and keep the existing
groupId-based key only as part of that composite so different schemas/save
targets mount distinct Settings instances.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant