From 1ddbbff8a46f10c63570f3e5833bbace56525c19 Mon Sep 17 00:00:00 2001 From: Dan Leech Date: Thu, 16 Jul 2026 17:24:44 +0100 Subject: [PATCH] Support aria-controls for buttons that toggle non-panel/popup UI Adds an `ariaControls` button-config prop (string or context-fn) that sets aria-controls directly, for buttons that expand a plugin-owned control rather than a panel or popup menu. Also scopes plugin dispatch to the originating plugin id so it routes correctly through the combined reducer when called from evaluated props. Extracted from the search-refactor branch so button+control consumers don't need to depend on that branch landing first. --- docs/api/button-definition.md | 13 +++++++++++++ src/App/components/MapButton/MapButton.jsx | 12 ++++++++++-- src/App/components/MapButton/MapButton.test.jsx | 10 ++++++++++ src/App/hooks/useEvaluateProp.js | 7 ++++++- src/App/hooks/useEvaluateProp.test.js | 12 ++++++++++-- src/App/renderer/mapButtons.js | 1 + src/types.js | 5 +++++ 7 files changed, 55 insertions(+), 5 deletions(-) diff --git a/docs/api/button-definition.md b/docs/api/button-definition.md index 499f4b0de..9281bc3dc 100644 --- a/docs/api/button-definition.md +++ b/docs/api/button-definition.md @@ -90,6 +90,19 @@ Associated panel identifier. When set, clicking the button toggles the panel ope --- +### `ariaControls` +**Type:** `string | function` + +Id of a custom control this button toggles, applied as `aria-controls`. Use when the button opens or expands a custom control rendered elsewhere by the plugin. Can be a string or a function that receives the [Context](./context.md) and returns the id. + +```js +ariaControls: (context) => `${context.appConfig.id}-search-form` +``` + +> Pair this with [`expandedWhen`](#expandedwhen) to also reflect the open/closed state via `aria-expanded`. + +--- + ### `keepFocus` **Type:** `boolean` **Default:** `false` diff --git a/src/App/components/MapButton/MapButton.jsx b/src/App/components/MapButton/MapButton.jsx index fe9237f1c..b847d5ad9 100755 --- a/src/App/components/MapButton/MapButton.jsx +++ b/src/App/components/MapButton/MapButton.jsx @@ -123,6 +123,9 @@ const getControlledElement = ({ idPrefix, panelId, buttonId, hasMenu }) => { * @param {boolean} options.isPanelOpen - Whether the controlled panel is open * @param {boolean} options.isPopupOpen - Whether the popup menu is open * @param {Object|null} options.controlledElement - The element controlled by this button + * @param {string} [options.ariaControls] - Explicit id for aria-controls when the button controls + * inline UI that is neither a panel nor a popup (e.g. a plugin-owned control). Ignored when + * controlledElement is set, which takes precedence. * @param {string} options.href - URL for anchor element (if provided, renders as instead of