From 7d3874728f88f99b2dab15c95245e1fa0febbb65 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 24 Oct 2026 22:02:12 +0000 Subject: [PATCH 1/3] chore(release): v1.9.0 Bump the plugin header, MAESTRO_VERSION, Stable tag and package version to 1.9.0. 1.8.0 was never tagged, so the 1.9.0 changelog and upgrade notice carry its changes, renamed to visibility groups, alongside "All other roles", preview as a role and the desktop and mobile polish through #260. Regenerate the POT, which still had the hiding-profile strings, and record the cut in STATE and the D5 row. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012tY8BSUE4tF9wo8m1hx4qW --- .planning/ROADMAP.md | 2 +- .planning/STATE.md | 9 + languages/maestro-menu-editor.pot | 573 +++++++++++++++--------------- maestro-menu-editor.php | 4 +- package-lock.json | 4 +- package.json | 2 +- readme.txt | 29 +- 7 files changed, 328 insertions(+), 295 deletions(-) diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 3c76ed6..1858c4e 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -544,7 +544,7 @@ should be made once. | D2 | Whether a request-scoped, viewer-only, subtract-only capability filter fits SPEC's "never changes a capability", or the wording gets a carve-out | Phase 30 | **Decided 2026-10-23 (Dan):** approve the narrow viewer-only, subtract-only menu-construction exception; see [decisions](phases/30-view-as-role-preview/30-CONTEXT.md) | | D3 | Label and form of the select-all control ("All other roles"; tri-state checkbox or a pair of links) | Phase 29 | **Decided 2026-10-23 (Dan):** a tri-state checkbox labelled "All other roles", as built in Phase 29 | | D4 | Import behaviour for rules whose menu item is missing on the target site: keep dormant, or drop and report | Phase 31 | **Decided 2026-10-23 (Dan): drop and report.** Rules naming a menu item the target site does not have are not stored; the import result lists them so the user knows what was not applied | -| D5 | Release shape: one v1.9.0 after all three, or cut after Phases 29–30 and let Phase 31 follow | Before the cut | Open — recommended 2026-10-23: cut v1.9.0 after Phases 29–30 once #248/#251 pass Dan's deep review; Phase 31 ships as a follow-up release. Awaiting Dan's confirmation | +| D5 | Release shape: one v1.9.0 after all three, or cut after Phases 29–30 and let Phase 31 follow | Before the cut | Recommended 2026-10-23: cut after Phases 29–30, Phase 31 follows. 2026-10-24: release PR cuts **v1.9.0** from main after #260 (1.8.0 was never tagged, so its changes ship in 1.9.0). Decided when Dan approves that PR | | D6 | Milestone version | Before the cut | **Decided 2026-10-22:** width v1.7.0, profiles v1.8, Client-Site Workflow v1.9 | ### Shared seams (build once, reuse) diff --git a/.planning/STATE.md b/.planning/STATE.md index 6c901a9..05142cb 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -16,6 +16,15 @@ progress: # Project State +## Release prep — 1.9.0 (2026-10-24) + +Main after #260 (`d34a576`) is being cut as **1.9.0**. 1.8.0 was bumped in #242 but +never tagged or released, so 1.9.0 carries visibility groups (Phase 27), the width +follow-ups, "All other roles" (Phase 29), preview as a role (Phase 30) and the +desktop and mobile polish (#256–#260). Phase 31 (export/import) follows in a later +release, per the D5 recommendation; the release PR is Dan's confirmation of D5. +The POT is regenerated. Real iPhone/iPad checks of #260 remain Dan's. + ## Desktop UX refinement — 2026-10-24 Phase 30 role-preview UI is on main through #251 (`9802065`). The desktop cleanup diff --git a/languages/maestro-menu-editor.pot b/languages/maestro-menu-editor.pot index 12cc1c2..cba7bca 100644 --- a/languages/maestro-menu-editor.pot +++ b/languages/maestro-menu-editor.pot @@ -2,14 +2,14 @@ # This file is distributed under the GPL-2.0-or-later. msgid "" msgstr "" -"Project-Id-Version: Maestro: The Inline Admin Menu Editor 1.8.0\n" -"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/maestro-menu-editor\n" +"Project-Id-Version: Maestro: The Inline Admin Menu Editor 1.9.0\n" +"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/Maestro\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2026-10-23T04:26:45+00:00\n" +"POT-Creation-Date: 2026-10-24T22:01:10+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "X-Generator: WP-CLI 2.12.0\n" "X-Domain: maestro-menu-editor\n" @@ -39,544 +39,561 @@ msgstr "" msgid "https://dan.knauss.ca/" msgstr "" -#: build/maestro-menu-editor/includes/class-admin-bar.php:97 -#: build/maestro-menu-editor/includes/class-admin-bar.php:114 -#: includes/class-admin-bar.php:97 -#: includes/class-admin-bar.php:114 +#: includes/class-admin-bar.php:73 +#: includes/class-preview.php:197 +msgid "Exit preview" +msgstr "" + +#: includes/class-admin-bar.php:75 +msgid "Exit role preview" +msgstr "" + +#: includes/class-admin-bar.php:110 +#: includes/class-admin-bar.php:127 msgid "Exit Menu Editor" msgstr "" -#: build/maestro-menu-editor/includes/class-admin-bar.php:99 -#: includes/class-admin-bar.php:99 +#: includes/class-admin-bar.php:112 msgid "Edit Admin Menu on the Dashboard" msgstr "" -#: build/maestro-menu-editor/includes/class-admin-bar.php:100 -#: includes/class-admin-bar.php:100 +#: includes/class-admin-bar.php:113 msgid "Edit Admin Menu" msgstr "" -#: build/maestro-menu-editor/includes/class-admin-bar.php:115 -#: includes/class-admin-bar.php:115 +#: includes/class-admin-bar.php:128 msgid "Edit Menu" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:249 -#: includes/class-assets.php:259 +#: includes/class-assets.php:273 msgid "Saving…" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:250 -#: includes/class-assets.php:260 +#: includes/class-assets.php:274 msgid "Saved" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:251 -#: includes/class-assets.php:261 +#: includes/class-assets.php:275 msgid "Save failed. Retrying on next change." msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:252 -#: includes/class-assets.php:262 +#: includes/class-assets.php:276 +msgid "Dismiss save error" +msgstr "" + +#: includes/class-assets.php:277 msgid "Edit Mode" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:253 -#: includes/class-assets.php:263 +#: includes/class-assets.php:278 msgid "Menu label" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:254 -#: includes/class-assets.php:264 +#: includes/class-assets.php:279 msgid "Icon" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:255 -#: includes/class-assets.php:265 +#: includes/class-assets.php:280 msgid "Choose an icon" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:256 -#: includes/class-assets.php:266 +#: includes/class-assets.php:281 msgid "Search icons" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:257 -#: includes/class-assets.php:267 +#: includes/class-assets.php:282 msgid "No icon" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:258 -#: includes/class-assets.php:268 +#: includes/class-assets.php:283 msgid "Remove the icon (uses the menu default)." msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:259 -#: includes/class-assets.php:269 +#: includes/class-assets.php:284 msgid "Visibility" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:260 -#: includes/class-assets.php:270 +#: includes/class-assets.php:285 msgid "Reset Item" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:261 -#: includes/class-assets.php:271 +#: includes/class-assets.php:286 msgid "Separator" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:262 -#: includes/class-assets.php:272 +#: includes/class-assets.php:287 msgid "Add separator below" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:263 -#: includes/class-assets.php:273 +#: includes/class-assets.php:288 msgid "Remove separator" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:264 -#: includes/class-assets.php:274 +#: includes/class-assets.php:289 msgid "Separator added" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:265 -#: includes/class-assets.php:275 +#: includes/class-assets.php:290 msgid "Separator removed" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:266 -#: includes/class-assets.php:276 +#: includes/class-assets.php:291 msgid "Reset All" msgstr "" #. translators: Heading for the role-checkbox group that hides THIS menu item (parent or submenu row). -#: build/maestro-menu-editor/includes/class-assets.php:268 -#: includes/class-assets.php:278 +#: includes/class-assets.php:293 msgid "Hide this item from:" msgstr "" #. translators: Heading for the role-checkbox group that cosmetically hides ALL of a parent's sub-items, independent of the parent's own visibility (COMPAT-10). Shown only for parents that have children. -#: build/maestro-menu-editor/includes/class-assets.php:270 -#: includes/class-assets.php:280 +#: includes/class-assets.php:295 msgid "Hide its sub-items from:" msgstr "" #. translators: %s: role display name. Explains why a "Hide its sub-items from:" checkbox is shown checked and disabled — the parent is already hidden from this role in the "Hide this item from:" group above, so its sub-items are already gone too (WordPress core removes a hidden parent's whole rendered subtree). -#: build/maestro-menu-editor/includes/class-assets.php:272 -#: includes/class-assets.php:282 +#: includes/class-assets.php:297 #, php-format msgid "Already hidden because this item is hidden from %s." msgstr "" +#. translators: Label of the tri-state checkbox at the top of each role group that ticks every role the editing user does not hold (Phase 29). +#: includes/class-assets.php:299 +msgid "All other roles" +msgstr "" + +#. translators: %s: comma-separated names of the editing user's own roles. Shown next to "All other roles" to say which roles it leaves alone. +#: includes/class-assets.php:301 +#, php-format +msgid "not %s" +msgstr "" + +#. translators: %s: the role group's heading, such as "Hide this item from:". Announced after "All other roles" ticks every other role. +#: includes/class-assets.php:303 +#, php-format +msgid "%s every other role is now ticked." +msgstr "" + +#. translators: %s: the role group's heading, such as "Hide this item from:". Announced after "All other roles" clears them. +#: includes/class-assets.php:305 +#, php-format +msgid "%s every other role is now cleared." +msgstr "" + +#. translators: %d: maximum number of roles in one visibility list. Shown when selecting every other role would exceed that limit. +#: includes/class-assets.php:307 +#, php-format +msgid "This would exceed the limit of %d roles per list. Choose roles individually." +msgstr "" + #. translators: Heading for the group that cosmetically hides THIS menu item from named individual people (ROLE-02), as opposed to whole roles. -#: build/maestro-menu-editor/includes/class-assets.php:274 -#: includes/class-assets.php:284 +#: includes/class-assets.php:309 msgid "Hide this item from specific people:" msgstr "" #. translators: Heading for the group that cosmetically hides ALL of a parent's sub-items from named individual people (ROLE-02). Shown only for parents that have children. -#: build/maestro-menu-editor/includes/class-assets.php:276 -#: includes/class-assets.php:286 +#: includes/class-assets.php:311 msgid "Hide its sub-items from specific people:" msgstr "" #. translators: Accessible label and placeholder for the type-ahead field used to find a person to hide a menu item from. -#: build/maestro-menu-editor/includes/class-assets.php:278 -#: includes/class-assets.php:288 +#: includes/class-assets.php:313 msgid "Search for a person" msgstr "" #. translators: Shown in the person picker when a search returns nobody. -#: build/maestro-menu-editor/includes/class-assets.php:280 -#: includes/class-assets.php:290 +#: includes/class-assets.php:315 msgid "No matching people found." msgstr "" #. translators: Shown in the person picker when the search request fails. -#: build/maestro-menu-editor/includes/class-assets.php:282 -#: includes/class-assets.php:292 +#: includes/class-assets.php:317 msgid "Could not search people. Please try again." msgstr "" #. translators: %s: person's display name. Accessible label for the button that removes that person from a hide rule. -#: build/maestro-menu-editor/includes/class-assets.php:284 -#: includes/class-assets.php:294 +#: includes/class-assets.php:319 #, php-format msgid "Remove %s" msgstr "" #. translators: Inline caution shown when an admin adds a hide rule against their OWN account. The rule is still permitted — this only warns. -#: build/maestro-menu-editor/includes/class-assets.php:286 -#: includes/class-assets.php:296 +#: includes/class-assets.php:321 msgid "This hides the item from your own account. You can still reach the page by URL, and the editor stays available." msgstr "" #. translators: %d: maximum number of people allowed per hide list. Shown when the editor tries to add one more than the limit. -#: build/maestro-menu-editor/includes/class-assets.php:288 -#: includes/class-assets.php:298 +#: includes/class-assets.php:323 #, php-format msgid "Limit reached — up to %d people per list. Remove someone to add another." msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:289 +#. translators: Heading for the group of checkboxes, one per hiding profile, that hide THIS menu item from everyone in the ticked profiles (Phase 27). +#: includes/class-assets.php:325 +msgid "Hide this item for visibility groups:" +msgstr "" + +#. translators: Heading for the group of checkboxes, one per hiding profile, that hide ALL of a parent's sub-items from everyone in the ticked profiles. Shown only for parents that have children. +#: includes/class-assets.php:327 +msgid "Hide its sub-items for visibility groups:" +msgstr "" + +#. translators: Shown in the Visibility popover when no hiding profiles exist yet. +#: includes/class-assets.php:329 +msgid "No visibility groups yet." +msgstr "" + +#. translators: Button in the Visibility popover and toolbar label that opens the dialog for creating and editing hiding profiles. +#: includes/class-assets.php:331 +msgid "Manage groups…" +msgstr "" + +#. translators: Title of the dialog for creating and editing hiding profiles (named groups of people that items can be hidden from). +#: includes/class-assets.php:333 +msgid "Visibility groups" +msgstr "" + +#. translators: Explanation at the top of the hiding-profiles dialog. #: includes/class-assets.php:335 +msgid "A visibility group lets you hide a menu item from everyone in the group. Add people to the group, then select it in the menu item's Visibility controls. This hides the menu link without changing page access." +msgstr "" + +#. translators: Persistent next-step guidance beside each saved hiding profile. +#: includes/class-assets.php:337 +msgid "Add people below, then select this group in a menu item's Visibility controls. Changes save automatically." +msgstr "" + +#. translators: Accessible label for the text field that renames a hiding profile. +#: includes/class-assets.php:339 +msgid "Group name" +msgstr "" + +#. translators: Placeholder and accessible label for the field where a new hiding profile's name is typed. +#: includes/class-assets.php:341 +msgid "New group name" +msgstr "" + +#. translators: Button that creates a hiding profile with the name typed beside it. +#: includes/class-assets.php:343 +msgid "Add group" +msgstr "" + +#. translators: %s: profile name. Heading for the list of people in that hiding profile. +#: includes/class-assets.php:345 +#, php-format +msgid "People in %s:" +msgstr "" + +#. translators: %s: profile name. Accessible label for the button that deletes that hiding profile. +#: includes/class-assets.php:347 +#, php-format +msgid "Delete %s" +msgstr "" + +#. translators: 1: profile name, 2: number of menu items the profile hides, 3: number of people in the profile. Confirmation before a hiding profile is deleted. +#: includes/class-assets.php:349 +#, php-format +msgid "Delete “%1$s”? Items it hides: %2$d. People in it: %3$d. Those people will see the items again." +msgstr "" + +#. translators: Caution shown in the hiding-profiles dialog while the editor's own account is among a profile's people. +#: includes/class-assets.php:351 +msgid "You are in this group, so the items it hides are hidden from your own account too. You can still reach those pages by URL, and the editor stays available." +msgstr "" + +#. translators: Shown when a hiding profile's name is left empty. +#: includes/class-assets.php:353 +msgid "Enter a name for the group." +msgstr "" + +#. translators: Shown when a hiding profile is given a name another profile already uses (names are compared ignoring case). +#: includes/class-assets.php:355 +msgid "Another group already has that name." +msgstr "" + +#. translators: %d: maximum number of hiding profiles. Shown when the limit is reached. +#: includes/class-assets.php:357 +#, php-format +msgid "Limit reached — up to %d groups." +msgstr "" + +#. translators: Screen-reader announcement after a hiding profile is created. +#: includes/class-assets.php:359 +msgid "Group added" +msgstr "" + +#. translators: Screen-reader announcement after a hiding profile is deleted. +#: includes/class-assets.php:361 +msgid "Group deleted" +msgstr "" + +#. translators: Label for the role picker that previews the menu as another role. +#: includes/class-assets.php:363 +#: includes/class-preview.php:189 +msgid "Preview as" +msgstr "" + +#: includes/class-assets.php:364 +#: includes/class-preview.php:196 +msgid "Preview" +msgstr "" + +#: includes/class-assets.php:365 +msgid "Role preview is not available to network super admins: WordPress grants them every capability, so their menu cannot be narrowed to a role." +msgstr "" + +#: includes/class-assets.php:366 +msgid "Your latest changes could not be saved, so the preview did not open. Try again once they are saved." +msgstr "" + +#: includes/class-assets.php:367 msgid "Reset ALL menu customizations to WordPress defaults? This cannot be undone." msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:290 -#: includes/class-assets.php:336 +#: includes/class-assets.php:368 msgid "Drag to reorder" msgstr "" #. translators: 1: item title, 2: direction ("up"/"down"), 3: new position number, 4: total items. -#: build/maestro-menu-editor/includes/class-assets.php:292 -#: includes/class-assets.php:338 +#: includes/class-assets.php:370 #, php-format msgid "%1$s moved %2$s, position %3$d of %4$d" msgstr "" #. translators: %s: item title. -#: build/maestro-menu-editor/includes/class-assets.php:294 -#: includes/class-assets.php:340 +#: includes/class-assets.php:372 #, php-format msgid "%s is already first" msgstr "" #. translators: %s: item title. -#: build/maestro-menu-editor/includes/class-assets.php:296 -#: includes/class-assets.php:342 +#: includes/class-assets.php:374 #, php-format msgid "%s is already last" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:297 -#: includes/class-assets.php:343 +#: includes/class-assets.php:375 msgid "up" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:298 -#: includes/class-assets.php:344 +#: includes/class-assets.php:376 msgid "down" msgstr "" #. translators: Accessible label for the "move selected item up" button in the editor panel. -#: build/maestro-menu-editor/includes/class-assets.php:300 -#: includes/class-assets.php:346 +#: includes/class-assets.php:378 msgid "Move up" msgstr "" #. translators: Accessible label for the "move selected item down" button in the editor panel. -#: build/maestro-menu-editor/includes/class-assets.php:302 -#: includes/class-assets.php:348 +#: includes/class-assets.php:380 msgid "Move down" msgstr "" #. translators: Short label appended to modified menu items for screen readers. -#: build/maestro-menu-editor/includes/class-assets.php:304 -#: includes/class-assets.php:350 +#: includes/class-assets.php:382 msgid "(modified)" msgstr "" #. translators: One-time hint shown to first-time users of the menu editor. -#: build/maestro-menu-editor/includes/class-assets.php:306 -#: includes/class-assets.php:352 +#: includes/class-assets.php:384 msgid "Click a menu item to start editing." msgstr "" #. translators: Label for the button that dismisses the first-run hint. -#: build/maestro-menu-editor/includes/class-assets.php:308 -#: includes/class-assets.php:354 +#: includes/class-assets.php:386 msgid "Got it" msgstr "" #. translators: Accessible label / tooltip for the toolbar button that replays the guided tour. -#: build/maestro-menu-editor/includes/class-assets.php:310 -#: includes/class-assets.php:356 +#: includes/class-assets.php:388 msgid "Show me around" msgstr "" #. translators: Title of the guided-tour tooltip dialog. -#: build/maestro-menu-editor/includes/class-assets.php:312 -#: includes/class-assets.php:358 +#: includes/class-assets.php:390 msgid "Editing the menu" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:313 -#: includes/class-assets.php:359 +#: includes/class-assets.php:391 msgid "Next" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:314 -#: includes/class-assets.php:360 +#: includes/class-assets.php:392 msgid "Back" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:315 -#: includes/class-assets.php:361 +#: includes/class-assets.php:393 msgid "Done" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:316 -#: includes/class-assets.php:362 -msgid "Skip" +#: includes/class-assets.php:394 +msgid "Close dialog" +msgstr "" + +#: includes/class-assets.php:395 +msgid "Close tour" msgstr "" #. translators: 1: current step number, 2: total steps. e.g. "2 of 5". -#: build/maestro-menu-editor/includes/class-assets.php:318 -#: includes/class-assets.php:364 +#: includes/class-assets.php:397 #, php-format msgid "%1$d of %2$d" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:319 -#: includes/class-assets.php:367 +#: includes/class-assets.php:400 msgid "Click any menu item to edit it right here on the menu." msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:320 -msgid "Rename it, change its icon, or hide it from roles using these controls." +#: includes/class-assets.php:401 +msgid "Type a new name for the selected item here." msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:321 -#: includes/class-assets.php:369 +#: includes/class-assets.php:402 msgid "Reorder items by dragging — or use the up and down arrows." msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:322 -msgid "Your changes save automatically. Reset one item, or Reset All to start over." +#: includes/class-assets.php:403 +msgid "Change the item's icon. Pick one from Dashicons or Bootstrap, or choose no icon." msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:323 -#: includes/class-assets.php:379 -msgid "Click Exit when you are done. Your changes stay live for everyone." +#: includes/class-assets.php:404 +msgid "Hide the item from chosen roles or people. Everyone else still sees it." msgstr "" -#. translators: Accessible label / tooltip for the toolbar button, and title of the dialog, holding menu-wide settings. -#: build/maestro-menu-editor/includes/class-assets.php:325 -#: includes/class-assets.php:381 -msgid "Menu settings" +#: includes/class-assets.php:405 +msgid "Undo every change to just this item." msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:326 -#: includes/class-assets.php:382 +#: includes/class-assets.php:406 +msgid "Add a separator line below this item. To remove one, select the separator and use the trash button." +msgstr "" + +#. translators: Guided-tour step pointing at the draggable edge of the admin menu. +#: includes/class-assets.php:408 +msgid "Drag the menu's edge to make it wider or narrower. When the edge has focus, the arrow keys work too." +msgstr "" + +#. translators: Guided-tour step pointing at the Visibility groups toolbar button. +#: includes/class-assets.php:410 +msgid "Visibility groups let you hide a menu item from everyone in a group. Add people to the group, then select it in the menu item's Visibility controls." +msgstr "" + +#: includes/class-assets.php:411 +msgid "Preview as shows the menu a role would see, read-only. Pick a role and press Preview; your changes are saved first." +msgstr "" + +#: includes/class-assets.php:412 +msgid "Menu width lets you type an exact width or go back to the default." +msgstr "" + +#: includes/class-assets.php:413 +msgid "Want this tour again? Use the help button anytime." +msgstr "" + +#: includes/class-assets.php:414 +msgid "Your changes save automatically. Reset All returns the whole menu to the WordPress defaults." +msgstr "" + +#: includes/class-assets.php:415 +msgid "Click Exit when you are done. Your changes stay live for everyone." +msgstr "" + +#. translators: Accessible label / tooltip for the toolbar button, and title of the dialog, holding menu-wide settings. +#: includes/class-assets.php:417 +#: includes/class-assets.php:418 msgid "Menu width" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:327 -#: includes/class-assets.php:383 +#: includes/class-assets.php:419 msgid "Menu width in pixels" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:328 -#: includes/class-assets.php:384 +#: includes/class-assets.php:420 msgid "Widen the menu so longer labels fit on one line. Applies on every admin page, for everyone, on screens 961px and wider." msgstr "" #. translators: %d: WordPress's default admin menu width in pixels. -#: build/maestro-menu-editor/includes/class-assets.php:330 -#: includes/class-assets.php:386 +#: includes/class-assets.php:422 #, php-format msgid "Using the WordPress default (%dpx)." msgstr "" #. translators: %d: the stored admin menu width in pixels. -#: build/maestro-menu-editor/includes/class-assets.php:332 -#: includes/class-assets.php:388 +#: includes/class-assets.php:424 #, php-format msgid "Custom width: %dpx." msgstr "" #. translators: %d: the width in pixels another plugin is holding the admin menu at. -#: build/maestro-menu-editor/includes/class-assets.php:334 -#: includes/class-assets.php:390 +#: includes/class-assets.php:426 #, php-format msgid "Another plugin sets the menu width (%dpx), so this setting has no effect while that plugin is active." msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:335 -#: includes/class-assets.php:391 +#: includes/class-assets.php:427 msgid "Use default" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:336 -#: includes/class-assets.php:392 +#: includes/class-assets.php:428 msgid "Menu width reset to the WordPress default." msgstr "" #. translators: Accessible label / tooltip for the handle on the admin menu's edge that resizes it. -#: build/maestro-menu-editor/includes/class-assets.php:338 -#: includes/class-assets.php:394 +#: includes/class-assets.php:430 msgid "Resize menu" msgstr "" #. translators: %d: a width in pixels, read out by screen readers. -#: build/maestro-menu-editor/includes/class-assets.php:340 -#: includes/class-assets.php:396 +#: includes/class-assets.php:432 #, php-format msgid "%d pixels" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:370 -#: includes/class-assets.php:511 +#: includes/class-assets.php:547 msgid "Dashicons" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:385 -#: includes/class-assets.php:526 +#: includes/class-assets.php:562 msgid "Bootstrap" msgstr "" #. translators: %s: name of another admin-menu editor plugin, e.g. "Admin Menu Editor". -#: build/maestro-menu-editor/includes/class-conflict-notice.php:62 #: includes/class-conflict-notice.php:62 #, php-format msgid "%s is also active. It rebuilds the admin menu too, so its settings can override or hide the changes you make here." msgstr "" -#: build/maestro-menu-editor/includes/class-rest.php:113 -#: includes/class-rest.php:113 -msgid "Invalid config payload." -msgstr "" - -#. translators: Heading for the group of checkboxes, one per hiding profile, that hide THIS menu item from everyone in the ticked profiles (Phase 27). -#: includes/class-assets.php:300 -msgid "Hide this item for visibility groups:" -msgstr "" - -#. translators: Heading for the group of checkboxes, one per hiding profile, that hide ALL of a parent's sub-items from everyone in the ticked profiles. Shown only for parents that have children. -#: includes/class-assets.php:302 -msgid "Hide its sub-items for visibility groups:" -msgstr "" - -#. translators: Shown in the Visibility popover when no hiding profiles exist yet. -#: includes/class-assets.php:304 -msgid "No visibility groups yet." -msgstr "" - -#. translators: Button in the Visibility popover and toolbar label that opens the dialog for creating and editing hiding profiles. -#: includes/class-assets.php:306 -msgid "Manage groups…" -msgstr "" - -#. translators: Title of the dialog for creating and editing hiding profiles (named groups of people that items can be hidden from). -#: includes/class-assets.php:308 -msgid "Visibility groups" -msgstr "" - -#. translators: Explanation at the top of the hiding-profiles dialog. -#: includes/class-assets.php:310 -msgid "A visibility group lets you hide a menu item from everyone in the group. Add people to the group, then select it in the menu item's Visibility controls. This hides the menu link without changing page access." -msgstr "" - -#. translators: Accessible label for the text field that renames a hiding profile. -#: includes/class-assets.php:312 -msgid "Group name" -msgstr "" - -#. translators: Placeholder and accessible label for the field where a new hiding profile's name is typed. -#: includes/class-assets.php:314 -msgid "New group name" -msgstr "" - -#. translators: Button that creates a hiding profile with the name typed beside it. -#: includes/class-assets.php:316 -msgid "Add group" -msgstr "" - -#. translators: %s: profile name. Heading for the list of people in that hiding profile. -#: includes/class-assets.php:318 -#, php-format -msgid "People in %s:" -msgstr "" - -#. translators: %s: profile name. Accessible label for the button that deletes that hiding profile. -#: includes/class-assets.php:320 -#, php-format -msgid "Delete %s" -msgstr "" - -#. translators: 1: profile name, 2: number of menu items the profile hides, 3: number of people in the profile. Confirmation before a hiding profile is deleted. -#: includes/class-assets.php:322 +#. translators: %s: role name, such as "Editor". +#: includes/class-preview.php:184 #, php-format -msgid "Delete “%1$s”? Items it hides: %2$d. People in it: %3$d. Those people will see the items again." -msgstr "" - -#. translators: Caution shown in the hiding-profiles dialog while the editor's own account is among a profile's people. -#: includes/class-assets.php:324 -msgid "You are in this group, so the items it hides are hidden from your own account too. You can still reach those pages by URL, and the editor stays available." -msgstr "" - -#. translators: Shown when a hiding profile's name is left empty. -#: includes/class-assets.php:326 -msgid "Enter a name for the group." -msgstr "" - -#. translators: Shown when a hiding profile is given a name another profile already uses (names are compared ignoring case). -#: includes/class-assets.php:328 -msgid "Another group already has that name." -msgstr "" - -#. translators: %d: maximum number of hiding profiles. Shown when the limit is reached. -#: includes/class-assets.php:330 -#, php-format -msgid "Limit reached — up to %d groups." -msgstr "" - -#. translators: Screen-reader announcement after a hiding profile is created. -#: includes/class-assets.php:332 -msgid "Group added" -msgstr "" - -#. translators: Screen-reader announcement after a hiding profile is deleted. -#: includes/class-assets.php:334 -msgid "Group deleted" -msgstr "" - -#: includes/class-assets.php:368 -msgid "Type a new name for the selected item here." -msgstr "" - -#: includes/class-assets.php:370 -msgid "Change the item's icon. Pick one from Dashicons or Bootstrap, or choose no icon." -msgstr "" - -#: includes/class-assets.php:371 -msgid "Hide the item from chosen roles or people. Everyone else still sees it." -msgstr "" - -#: includes/class-assets.php:372 -msgid "Undo every change to just this item." +msgid "Previewing menu as %s — read-only" msgstr "" -#: includes/class-assets.php:373 -msgid "Add a separator line below this item. To remove one, select the separator and use the trash button." +#: includes/class-preview.php:187 +msgid "The menu you see now is a representation of how it should appear to users in the role you’ve selected. This preview is based on the rules WordPress and your plugins build for this role, with Maestro’s rules applied last. Keep in mind it’s an approximation: rules for specific users or visibility groups, capabilities given to individual users, people with more than one role, and plugins that check the user directly can create different, unexpected results. Clicking a menu item still opens the page with your own permissions." msgstr "" -#. translators: Guided-tour step pointing at the draggable edge of the admin menu. -#: includes/class-assets.php:375 -msgid "Drag the menu's edge to make it wider or narrower. When the edge has focus, the arrow keys work too." +#: includes/class-preview.php:211 +msgid "That role preview link has expired. Choose the role again from the menu editor." msgstr "" -#: includes/class-assets.php:376 -msgid "Menu settings let you type an exact menu width or go back to the default." +#: includes/class-preview.php:212 +msgid "That role cannot be previewed on this site." msgstr "" -#: includes/class-assets.php:377 -msgid "Want this tour again? Use the help button anytime." +#: includes/class-preview.php:213 +msgid "That role has no access to the WordPress admin, so there is no menu to preview." msgstr "" -#: includes/class-assets.php:378 -msgid "Your changes save automatically. Reset All returns the whole menu to the WordPress defaults." +#: includes/class-preview.php:214 +msgid "Role preview is not available to network super admins. Use a site administrator account to preview this site’s roles." msgstr "" -#. translators: Persistent next-step guidance beside each saved hiding profile. -#: includes/class-assets.php:327 -msgid "Add people below, then select this group in a menu item's Visibility controls. Changes save automatically." +#: includes/class-preview.php:215 +msgid "Role preview runs on the Dashboard. Choose the role again from the menu editor." msgstr "" -#. translators: Guided-tour step pointing at the Visibility groups toolbar button. -#: includes/class-assets.php:394 -msgid "Visibility groups let you hide a menu item from everyone in a group. Add people to the group, then select it in the menu item's Visibility controls." +#: includes/class-rest.php:113 +msgid "Invalid config payload." msgstr "" diff --git a/maestro-menu-editor.php b/maestro-menu-editor.php index 9205158..76f59c5 100644 --- a/maestro-menu-editor.php +++ b/maestro-menu-editor.php @@ -3,7 +3,7 @@ * Plugin Name: Maestro: The Inline Admin Menu Editor * Plugin URI: https://github.com/dknauss/Maestro/ * Description: In-place editing of the WordPress admin menu — rename items, reorder them, swap top-level icons, and hide items per role. Cosmetic only: hiding declutters, it does not lock access. - * Version: 1.8.0 + * Version: 1.9.0 * Requires at least: 6.4 * Requires PHP: 7.4 * Author: Dan Knauss @@ -20,7 +20,7 @@ defined( 'ABSPATH' ) || exit; -define( 'MAESTRO_VERSION', '1.8.0' ); +define( 'MAESTRO_VERSION', '1.9.0' ); define( 'MAESTRO_FILE', __FILE__ ); define( 'MAESTRO_DIR', plugin_dir_path( __FILE__ ) ); define( 'MAESTRO_URL', plugin_dir_url( __FILE__ ) ); diff --git a/package-lock.json b/package-lock.json index dc2ca4f..a3301a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "maestro", - "version": "1.8.0", + "version": "1.9.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "maestro", - "version": "1.8.0", + "version": "1.9.0", "devDependencies": { "@axe-core/playwright": "^4.14.0", "@playwright/test": "^1.64.1", diff --git a/package.json b/package.json index cbdf6de..a0a7afb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "maestro", - "version": "1.8.0", + "version": "1.9.0", "private": true, "description": "Tooling for Maestro: The Inline Admin Menu Editor: wp-env, PHP integration tests, Playwright E2E.", "scripts": { diff --git a/readme.txt b/readme.txt index 3d7e93b..8681f0e 100644 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Donate link: https://github.com/sponsors/dknauss Tags: admin menu, menu editor, hide menu items, rename menu items, menu icons Requires at least: 6.4 Tested up to: 7.1 -Stable tag: 1.8.0 +Stable tag: 1.9.0 Requires PHP: 7.4 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html @@ -76,11 +76,11 @@ named individuals, so what each person sees can differ. The rename/reorder/icon = Can I see the menu the way an Editor would? = -Yes. In the editor toolbar, pick a role under **Preview as** and press -**Preview**. The Dashboard shows that role's menu, with your rules for the role +Yes. Press **Preview as** (the eye button in the editor toolbar), choose a +role and press **Preview**. The Dashboard shows that role's menu, with your rules for the role applied, under a read-only banner with **Exit preview**. You stay signed in as yourself and nothing is saved. It is an approximation: rules for specific -people or Visibility groups, capabilities given to individual users, people with several +people or visibility groups, capabilities given to individual users, people with several roles, and plugins that check the user directly can differ. Network super admins on multisite cannot preview. @@ -207,13 +207,20 @@ If Maestro saves you time or brings you or your clients the joy of a tidy admin == Changelog == -= 1.8.0 = -* **New: hiding profiles.** A hiding profile is a named group of people, such as "Reduced view" for a client's staff. Open **Hiding profiles** (the people button in the editing toolbar), give the profile a name and add people to it. Then tick it in any item's Visibility popover under "Hide this item for profiles:" or, on an item with children, "Hide its sub-items for profiles:". If you add or remove someone later, every item the profile hides follows right away. Deleting a profile tells you how many items and people it affects first. -* A profile is not a role. WordPress never sees it, and it grants or removes nothing. Like every hide in Maestro it is cosmetic, so a hidden page still opens by URL for anyone allowed to see it. Up to 20 profiles, with up to 50 people each. -* Only people who can list users (administrators, by default) see or edit profiles. Anyone else editing the menu keeps the stored profiles as they are. On a multisite network, network super admins are exempt from profile hides, as they already are from hides that name a person. += 1.9.0 = +1.8.0 was never released on its own, so this release carries its changes too. + +* **New: preview the menu as a role.** Press **Preview as** (the eye button in the editing toolbar), choose a role and press **Preview**. The Dashboard then shows that role's menu with your rules applied, under a read-only banner with **Exit preview**. You stay signed in as yourself, nothing is saved, and anything still pending is saved before the preview opens. It is an approximation: rules for specific people or visibility groups, capabilities given to individual users, people with several roles, and plugins that check the user directly can differ. Previews run on the Dashboard only, for roles that can reach the admin. Network super admins on multisite cannot preview. +* **New: "All other roles" in every role list.** One checkbox at the top ticks or clears every role except your own. It shows a mixed state when only some are ticked. If ticking it would go past the 50-role limit for a list, nothing changes and Maestro tells you why, rather than saving part of the list. +* **New: visibility groups.** A visibility group is a named group of people, such as "Reduced view" for a client's staff. Open **Visibility groups** (the people button in the editing toolbar), give the group a name and add people to it. Then tick it in any item's Visibility popover under "Hide this item for visibility groups:" or, on an item with children, "Hide its sub-items for visibility groups:". If you add or remove someone later, every item the group hides follows right away. Deleting a group tells you how many items and people it affects first. The dialog and the help tour explain what a saved group does. +* A visibility group is not a role. WordPress never sees it, and it grants or removes nothing. Like every hide in Maestro it is cosmetic, so a hidden page still opens by URL for anyone allowed to see it. Up to 20 groups, with up to 50 people each. +* Only people who can list users (administrators, by default) see or edit visibility groups. Anyone else editing the menu keeps the stored groups as they are. On a multisite network, network super admins are exempt from group hides, as they already are from hides that name a person. * **Fixed: people who can edit the menu but not list users were sent the user IDs named in per-user hides.** This only matters when a site gives Maestro editing to a role without the `list_users` capability. Those editors now get the menu without that data, and saving keeps the hides they cannot see. +* **Improved: phones and tablets.** Tapping a menu item to select it no longer opens the on-screen keyboard. While you rename an item, the editing toolbar rises above the keyboard, and a typed name is kept when you tap another item or turn the device. Dialogs on a phone get a header with a close button. Tablets and other touch screens wider than a phone get the same touch handling. * **Improved: the help tour gives every editing control its own tip,** and on a narrow screen it skips controls that are not shown. -* **Improved: the Visibility popover and the Hiding profiles dialog scroll inside themselves** on a short screen instead of running off it. +* **Improved: dialogs that save as you go** (Visibility groups, Menu settings and the help tour) have a close button in their header, and save messages float above the editing toolbar instead of covering it. Toolbar hover colours follow your admin colour scheme. +* **Improved: a save error that stays on screen can be dismissed** with the × beside it. That only hides the message: the save still counts as failed, Preview still waits for a good save, and the message comes back with the next save attempt. +* **Improved: the Visibility popover and the Visibility groups dialog scroll inside themselves** on a short screen instead of running off it. * **Fixed: on a phone, the help tour jumped from its second tip to its last.** Tapping Next closed WordPress's phone menu, which took the editing toolbar with it, so the tour had nothing left to point at. Every tip now shows, and the count leaves out only the width handle and Menu settings, which phones do not have. * **Fixed: on a phone, a tap inside the icon picker or the Visibility popover closed the editor.** The same cause as the tour. On a phone on its side, the icon picker also opened below the bottom of the screen; it now fits above the toolbar and scrolls. * **Improved: the editing toolbar is smaller on phones.** Upright, it takes two rows instead of three or four: the name field with the move buttons, then every other control. On its side it takes one. The add and remove separator buttons and, on a phone held upright, Reset All show only their icons there, and keep their names for screen readers and as tooltips. @@ -328,8 +335,8 @@ If Maestro saves you time or brings you or your clients the joy of a tidy admin == Upgrade Notice == -= 1.8.0 = -Adds hiding profiles: name a group of people, then hide items from the whole group in one tick. Profiles are not roles and grant nothing. Fixes per-user hide data reaching menu editors who cannot list users. Cosmetic only: no access changed. += 1.9.0 = +Adds preview as a role, an "All other roles" checkbox in every role list, and visibility groups for hiding items from a named group of people. Improves editing on phones and tablets. Fixes per-user hide data reaching menu editors who cannot list users. Cosmetic only: no access changed. = 1.7.0 = Adds a menu width setting: 160 to 320px, set from Menu settings or by dragging the menu edge, applied on every admin page. Stands aside when another plugin sets the width. Fixes the first menu row hiding under the admin bar on phones. Cosmetic only. From ae68064a3ef31b7d97bea3df3a933003810e66d6 Mon Sep 17 00:00:00 2001 From: Dan Knauss <273554+dknauss@users.noreply.github.com> Date: Sun, 25 Oct 2026 11:09:06 -0600 Subject: [PATCH 2/3] Unify dialog controls and compact the phone toolbar --- assets/maestro-logic.js | 16 + assets/maestro.css | 79 ++- assets/maestro.js | 37 +- docs/design/ui-standard.md | 41 ++ docs/testing/iphone-issues.md | 50 ++ docs/user-guide.md | 11 + includes/class-assets.php | 25 +- languages/maestro-menu-editor.pot | 599 ++++++++++++----------- tests/e2e/dialog-standard.spec.ts | 98 ++++ tests/e2e/mobile-usability.spec.ts | 6 +- tests/e2e/specs/phone-layout.spec.ts | 48 +- tests/e2e/tour.spec.ts | 2 +- tests/js/profile-delete-message.test.mjs | 29 ++ 13 files changed, 696 insertions(+), 345 deletions(-) create mode 100644 docs/design/ui-standard.md create mode 100644 docs/testing/iphone-issues.md create mode 100644 tests/e2e/dialog-standard.spec.ts create mode 100644 tests/js/profile-delete-message.test.mjs diff --git a/assets/maestro-logic.js b/assets/maestro-logic.js index ecc0ad8..e6c9c38 100644 --- a/assets/maestro-logic.js +++ b/assets/maestro-logic.js @@ -413,6 +413,21 @@ function profileNameProblem( name, profiles, exceptId ) { return taken ? 'taken' : ''; } +/** Build a deletion confirmation without promising visibility changes. */ +function profileDeleteMessage( name, items, people, strings ) { + var template = items === 1 ? + ( people === 1 ? strings.profileDeleteBoth : strings.profileDeleteItem ) : + ( people === 1 ? strings.profileDeletePerson : strings.profileDeleteConfirm ); + var message = template + .replace( '%1$s', function () { return name; } ) + .replace( '%2$d', function () { return String( items ); } ) + .replace( '%3$d', function () { return String( people ); } ); + if ( items > 0 && people > 0 ) { + message += ' ' + strings.profileDeleteEffect; + } + return message; +} + /** * How many items a profile hides, counted from the editor's assignments: an * item hidden through both its own and its sub-items axis counts once. @@ -728,6 +743,7 @@ var api = { dropProfileCarry: dropProfileCarry, profileNameProblem: profileNameProblem, profileItemCount: profileItemCount, + profileDeleteMessage: profileDeleteMessage, buildProfilesPayload: buildProfilesPayload, carryProfileHides: carryProfileHides, carrySeparators: carrySeparators, diff --git a/assets/maestro.css b/assets/maestro.css index 1abd170..4f72555 100644 --- a/assets/maestro.css +++ b/assets/maestro.css @@ -1483,6 +1483,83 @@ body.maestro-editing #adminmenu li.menu-top .wp-submenu { @media ( hover: hover ) { .maestro-toolbar .button:not(:disabled):not(.button-link-delete):hover { background: var( --maestro-menu-accent, #2271b1 ); - color: var( --maestro-menu-accent-text, #fff ); } } + +/* Dialog standard: shared geometry and one focus treatment, including controls + * supplied by WordPress. Native checkboxes/radios keep their familiar shapes. */ +.wp-core-ui :is(.maestro-popover, .maestro-tour) { + --maestro-control-radius: 3px; + --maestro-focus-color: #2271b1; +} +.wp-core-ui :is(.maestro-popover, .maestro-tour) :is(button.button, button.button-link, button, input[type='text'], input[type='search'], input[type='number'], input:not([type]), select) { + border-radius: var(--maestro-control-radius); +} +.wp-core-ui :is(.maestro-popover, .maestro-tour) :is(button.button, button.button-link, button, input, select, [tabindex]):focus { + box-shadow: none; + outline: none; +} +.wp-core-ui :is(.maestro-popover, .maestro-tour) :is(button.button, button.button-link, button, input, select, [tabindex]):focus-visible { + outline: 2px solid var(--maestro-focus-color); + outline-offset: 2px; +} +/* Safari may consider programmatic focus after a tap focus-visible. Keep focus + * for containment without drawing a keyboard ring until keyboard input resumes. + * Text fields retain their focus indicator, including while typing by touch. */ +.maestro-pointer-input .wp-core-ui :is(.maestro-popover, .maestro-tour) :is(button.button, button.button-link, button, [tabindex]:not(input):not(select):not(textarea)):focus { + box-shadow: none; + outline: none; +} + +/* Toolbar icons retain their neutral/destructive colours in every state. + * Background indicates the open tool independently of pointer focus. */ +.maestro-toolbar .button:not(:disabled):not(.button-link-delete):is(:hover, :focus, :active) { + color: #c3c4c7; +} +.maestro-toolbar .button:not(:disabled):is(:focus-visible, :active, [aria-expanded="true"]) { + background: var(--maestro-menu-accent, #2271b1); +} +@media (hover: hover) { + .maestro-toolbar .button.button-link-delete:hover { + background: var(--maestro-menu-accent, #2271b1); + } +} + +/* Phone tools occupy two bounded rows, or one on short landscape screens. + * Each group scrolls horizontally rather than wrapping or shrinking targets. */ +@media screen and (max-width: 782px), screen and (max-width: 960px) and (max-height: 500px) { + .maestro-toolbar { + flex-direction: column; + flex-wrap: nowrap; + align-items: stretch; + gap: 4px; + } + .maestro-toolbar .maestro-panel, + .maestro-toolbar .maestro-toolbar-right { + display: flex; + flex: 0 0 auto; + flex-wrap: nowrap; + align-items: center; + max-width: 100%; + min-width: 0; + gap: 4px; + margin: 0; + padding: 2px; + box-sizing: border-box; + overflow-x: auto; + overscroll-behavior-x: contain; + } + .maestro-toolbar .maestro-panel[hidden] { display: none; } + .maestro-toolbar .maestro-panel > *, + .maestro-toolbar .maestro-toolbar-right > * { flex-shrink: 0; } + .maestro-toolbar .maestro-toolbar-right > :first-child { margin-inline-start: 0; } + .maestro-toolbar .maestro-rename-input { flex: 0 0 128px; width: 128px; min-height: 44px; } +} +@media screen and (max-width: 960px) and (max-height: 500px) and (orientation: landscape) { + .maestro-toolbar { flex-direction: row; } + .maestro-toolbar .maestro-panel { flex: 1 1 auto; } + .maestro-toolbar .maestro-toolbar-right { flex: 0 0 auto; } +} +@media screen and (max-width: 782px), screen and (max-width: 960px) and (max-height: 500px) { + .maestro-toolbar .maestro-tour-help { display: none; } +} diff --git a/assets/maestro.js b/assets/maestro.js index 2fa15fd..cd9c4fa 100644 --- a/assets/maestro.js +++ b/assets/maestro.js @@ -97,6 +97,7 @@ n.dir = 'auto'; } function removePopover( pop ) { + if ( pop.maestroAnchor ) { pop.maestroAnchor.setAttribute( 'aria-expanded', 'false' ); } if ( pop.maestroCleanup ) { pop.maestroCleanup(); } pop.remove(); } @@ -831,7 +832,6 @@ if ( current ) { var scheme = window.getComputedStyle( current ); bar.style.setProperty( '--maestro-menu-accent', scheme.backgroundColor ); - bar.style.setProperty( '--maestro-menu-accent-text', scheme.color ); } // Transient save-status — aria-live, empty at idle so no announcement fires. @@ -1067,6 +1067,10 @@ var parent = menuWrap && hamburger && hamburger.offsetParent !== null ? menuWrap : document.body; if ( bar.parentNode !== parent ) { parent.appendChild( bar ); } } + bar.querySelectorAll( '.maestro-icon-btn, .maestro-vis-btn, .maestro-settings-btn, .maestro-profiles-btn, .maestro-preview-toggle, .maestro-tour-help' ).forEach( function ( button ) { + button.setAttribute( 'aria-haspopup', 'dialog' ); + button.setAttribute( 'aria-expanded', 'false' ); + } ); mountToolbar(); window.addEventListener( 'resize', mountToolbar ); @@ -1793,10 +1797,7 @@ var count = window.maestroLogic.profileItemCount( p.id, assignments() ); // Function replacers: a name is user text, and a "$&" in it would // otherwise be read as a replacement pattern. - var question = I.profileDeleteConfirm - .replace( '%1$s', function () { return p.name; } ) - .replace( '%2$d', function () { return String( count ); } ) - .replace( '%3$d', function () { return String( p.users.length ); } ); + var question = window.maestroLogic.profileDeleteMessage( p.name, count, p.users.length, I ); if ( ! window.confirm( question ) ) { return; } deleteProfile( p.id ); renderList(); @@ -2815,6 +2816,9 @@ } function placePopover( pop, anchorBtn ) { + if ( tour && tour.active ) { endTour(); } + pop.maestroAnchor = anchorBtn; + anchorBtn.setAttribute( 'aria-expanded', 'true' ); if ( isPhoneViewport() ) { return placeMobilePopover( pop, anchorBtn ); } @@ -2953,6 +2957,9 @@ var bar = document.getElementById( 'wpadminbar' ); var top = Math.max( ( v ? v.offsetTop : 0 ) + 8, bar ? bar.getBoundingClientRect().bottom + 8 : 8 ); var bottom = ( v ? v.offsetTop + v.height : window.innerHeight ) - 8; + // Keep the other tool buttons available even when this is a long sheet. + var toolbar = document.querySelector( '.maestro-toolbar' ); + if ( toolbar ) { bottom = Math.min( bottom, toolbar.getBoundingClientRect().top - 8 ); } pop.style.top = top + 'px'; pop.style.width = Math.max( 0, ( v ? v.width : window.innerWidth ) - 16 ) + 'px'; pop.style.height = 'auto'; @@ -2965,6 +2972,9 @@ } function outside( e ) { if ( ! pop.contains( e.target ) ) { + // Tool buttons replace this dialog through their own click handler. + // Other page controls remain inert while the phone sheet is open. + if ( e.target.closest( '.maestro-toolbar [aria-haspopup="dialog"]:not(:disabled)' ) ) { return; } e.preventDefault(); e.stopPropagation(); } @@ -3278,7 +3288,7 @@ var toggle = el( 'button', 'button maestro-preview-toggle' ); toggle.type = 'button'; toggle.setAttribute( 'aria-haspopup', 'dialog' ); - iconButton( toggle, 'dashicons-visibility', I.previewAs ); + iconButton( toggle, 'dashicons-welcome-view-site', I.previewAs ); toggle.addEventListener( 'click', function () { openPreviewDialog( toggle ); } ); wrap.appendChild( toggle ); return wrap; @@ -3325,8 +3335,14 @@ // Touch can come from a tablet or touch laptop as well as a phone. var lastPointerType = ''; - document.addEventListener( 'pointerdown', function ( e ) { lastPointerType = e.pointerType; }, true ); - document.addEventListener( 'keydown', function () { lastPointerType = ''; }, true ); + document.addEventListener( 'pointerdown', function ( e ) { + lastPointerType = e.pointerType; + document.documentElement.classList.add( 'maestro-pointer-input' ); + }, true ); + document.addEventListener( 'keydown', function () { + lastPointerType = ''; + document.documentElement.classList.remove( 'maestro-pointer-input' ); + }, true ); function touchPointer() { return lastPointerType === 'touch' || lastPointerType === 'pen'; } @@ -3556,6 +3572,9 @@ function startTour() { if ( tour.active ) { return; } + closePopovers(); + var help = document.querySelector( '.maestro-tour-help' ); + if ( help ) { help.setAttribute( 'aria-expanded', 'true' ); } // A step the viewport hides (byViewport) is dropped before the tour // starts, so "3 of 11" counts only what will be shown rather than // jumping from 7 to 9 on a phone. A panel control hidden only for the @@ -3575,6 +3594,8 @@ } function endTour() { + var help = document.querySelector( '.maestro-tour-help' ); + if ( help ) { help.setAttribute( 'aria-expanded', 'false' ); } tour.active = false; if ( tour.root ) { tour.root.remove(); tour.root = null; } document.removeEventListener( 'keydown', onTourKeydown, true ); diff --git a/docs/design/ui-standard.md b/docs/design/ui-standard.md new file mode 100644 index 0000000..8418301 --- /dev/null +++ b/docs/design/ui-standard.md @@ -0,0 +1,41 @@ +# Dialog UI standard + +All editor popovers and the guided tour use the shared rules at the end of +`assets/maestro.css`. Avoid per-dialog overrides of control geometry or focus. + +- Buttons, text/number/search fields and selects use the 3px + `--maestro-control-radius` token. Cards keep their own radius; native + checkboxes and radio buttons keep their native shapes. +- Keyboard focus uses a 2px blue outline with a 2px offset, without a second + box-shadow ring. Pointer interaction hides button focus rings; the next + keyboard interaction restores them. Text fields retain visible focus. +- Touch entry keeps focus on Close (or the dialog container when no Close + exists), so opening a dialog does not open the software keyboard. Close + targets remain at least 44 by 44 CSS pixels. Never blur focus to hide a ring. +- Use sentence case for headings, labels and actions: “Visibility groups”, + “Add group”, “Reset all”. Preserve proper names such as WordPress and Dashicons. +- In explanatory text, reproduce a named control's label exactly, for example + “the menu item's Visibility settings”. Generic uses stay lowercase, as in + “visibility rules”. Use “people” in visible group membership labels. +- Explain only consequences supported by the current state. Deleting a group + with no assigned items or no members must not claim to change anyone's menu. + Other visibility rules may still apply, so never promise restored visibility. +- Keep complete translatable sentences in `includes/class-assets.php`. + Conditional consequences use separate complete sentences, not word fragments. + +Regression coverage: dialog-standard E2E tests cover pointer/keyboard focus and +control radii across dialogs and phone orientations; profile deletion unit tests +cover all combinations of zero/nonzero item and member counts. + +Toolbar tools preserve their icon colours on hover, focus and activation; use +background colour for state, with `aria-expanded` tied to the open dialog. +Phone tool groups scroll rather than wrapping beyond two portrait rows or one +short-landscape row; controls keep 44px touch targets. +The coach tips (`?`) launcher is omitted on phones, including short landscape +screens; it remains available on larger screens. + +Shared label casing and deletion-message invariants are also enforced in +`tests/js/profile-delete-message.test.mjs`. +Use distinct glyphs for different tools: an eye for Visibility rules, a monitor +for Preview as, and a group of people for Visibility groups. Accessible names +remain explicit even when visible labels are hidden. diff --git a/docs/testing/iphone-issues.md b/docs/testing/iphone-issues.md new file mode 100644 index 0000000..24bb0ff --- /dev/null +++ b/docs/testing/iphone-issues.md @@ -0,0 +1,50 @@ +# iPhone acceptance issues + +## 1. Dialog focus, geometry and wording + +- Orientation: portrait; landscape not yet reported by Dan. +- Reproduce: open Visibility groups from the toolbar. +- Actual: Close shows a focus border immediately after a tap, its radius differs + from Add group, and the help text refers to “Visibility controls”. +- Expected: shared button geometry, keyboard-only button focus indicators, + sentence case, and consistent references to named UI controls. +- Status: fix implemented; awaiting device verification. + +## 2. Empty-group deletion claims restored visibility + +- Orientation: portrait. +- Reproduce: create a group with no assigned menu items and no members; delete it. +- Actual: confirmation says “Those people will see the items again.” +- Expected: “Delete ‘Group name’? It hides 0 items from 0 people.” Only describe + removal of visibility rules when both counts are nonzero; never promise that + overlapping role, person or other group rules will stop hiding the items. +- Status: fix implemented; awaiting device verification. + +## 3. Toolbar tool switching and icon colours + +- Orientation: portrait; also test landscape. +- Reproduce: open Preview, then tap Visibility groups in the toolbar. +- Actual: Preview remains open; focus styling changes on the toolbar instead. +- Expected: the newly chosen tool replaces the dialog, and only the open tool + has an active background. Preserve icon colours in hover/focus/active states. +- Cause: the mobile sheet's document capture handler swallowed toolbar clicks. +- Status: fix implemented; awaiting device verification. + +## 4. Toolbar wrapping crowds out the menu + +- Orientation: portrait and landscape. +- Reproduce: select a menu item; rotate to landscape. +- Actual: three portrait rows or two landscape rows consume the editing area. +- Expected: at most two portrait rows and one short-landscape row, preserving + 44px targets. Tool groups scroll horizontally when needed. +- Status: fix implemented; awaiting device verification. + +## 5. Duplicate eye icons for unrelated tools + +- Orientation: both; visible in the portrait and landscape reports above. +- Reproduce: select a menu item and compare Visibility with Preview as. +- Actual: both tools use an eye glyph despite performing different actions. +- Expected: retain the eye for Visibility rules; use a monitor with an eye for Preview as, + with distinct accessible labels and stable icon colours. +- Introduced: role preview in PR #251; retained by PR #260. +- Status: fix implemented; awaiting device verification. diff --git a/docs/user-guide.md b/docs/user-guide.md index 17deccd..f81b4bd 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -218,3 +218,14 @@ the menu to the defaults generated by WordPress and active plugins. Because the plugin stores sparse deltas instead of a rebuilt menu, reset is safe: when the saved option is removed, the natural menu returns automatically. + +### Phone toolbar and dialogs + +On phones, the toolbar uses up to two rows in portrait and one row in short +landscape viewports. Swipe the item tools sideways if they do not all fit; touch +targets remain full size. Coach tips are available from the `?` button on larger +screens. Tap another tool to switch dialogs. The open tool has a coloured +background while its icon keeps its usual colour. + +Dialogs share button shapes and keyboard focus indicators. Opening one by touch +keeps the keyboard closed; tap a text field when you want to type. diff --git a/includes/class-assets.php b/includes/class-assets.php index 505ce2a..eef35fa 100644 --- a/includes/class-assets.php +++ b/includes/class-assets.php @@ -274,7 +274,7 @@ public function enqueue() { 'saved' => __( 'Saved', 'maestro-menu-editor' ), 'saveError' => __( 'Save failed. Retrying on next change.', 'maestro-menu-editor' ), 'dismissSaveError' => __( 'Dismiss save error', 'maestro-menu-editor' ), - 'modeLabel' => __( 'Edit Mode', 'maestro-menu-editor' ), + 'modeLabel' => __( 'Edit mode', 'maestro-menu-editor' ), 'renamePlaceholder' => __( 'Menu label', 'maestro-menu-editor' ), 'icon' => __( 'Icon', 'maestro-menu-editor' ), 'iconDialog' => __( 'Choose an icon', 'maestro-menu-editor' ), @@ -282,13 +282,13 @@ public function enqueue() { 'iconNone' => __( 'No icon', 'maestro-menu-editor' ), 'iconNoneHint' => __( 'Remove the icon (uses the menu default).', 'maestro-menu-editor' ), 'visibility' => __( 'Visibility', 'maestro-menu-editor' ), - 'resetItem' => __( 'Reset Item', 'maestro-menu-editor' ), + 'resetItem' => __( 'Reset item', 'maestro-menu-editor' ), 'separator' => __( 'Separator', 'maestro-menu-editor' ), 'addSeparator' => __( 'Add separator below', 'maestro-menu-editor' ), 'removeSeparator' => __( 'Remove separator', 'maestro-menu-editor' ), 'separatorAdded' => __( 'Separator added', 'maestro-menu-editor' ), 'separatorRemoved' => __( 'Separator removed', 'maestro-menu-editor' ), - 'resetAll' => __( 'Reset All', 'maestro-menu-editor' ), + 'resetAll' => __( 'Reset all', 'maestro-menu-editor' ), /* translators: Heading for the role-checkbox group that hides THIS menu item (parent or submenu row). */ 'hideFrom' => __( 'Hide this item from:', 'maestro-menu-editor' ), /* translators: Heading for the role-checkbox group that cosmetically hides ALL of a parent's sub-items, independent of the parent's own visibility (COMPAT-10). Shown only for parents that have children. */ @@ -332,9 +332,9 @@ public function enqueue() { /* translators: Title of the dialog for creating and editing hiding profiles (named groups of people that items can be hidden from). */ 'profiles' => __( 'Visibility groups', 'maestro-menu-editor' ), /* translators: Explanation at the top of the hiding-profiles dialog. */ - 'profilesIntro' => __( 'A visibility group lets you hide a menu item from everyone in the group. Add people to the group, then select it in the menu item\'s Visibility controls. This hides the menu link without changing page access.', 'maestro-menu-editor' ), + 'profilesIntro' => __( 'A visibility group lets you hide a menu item from everyone in the group. Add people to the group, then select the group in the menu item\'s Visibility settings. This hides the menu link without changing page access.', 'maestro-menu-editor' ), /* translators: Persistent next-step guidance beside each saved hiding profile. */ - 'profileGuidance' => __( 'Add people below, then select this group in a menu item\'s Visibility controls. Changes save automatically.', 'maestro-menu-editor' ), + 'profileGuidance' => __( 'Add people below, then select this group in a menu item\'s Visibility settings. Changes save automatically.', 'maestro-menu-editor' ), /* translators: Accessible label for the text field that renames a hiding profile. */ 'profileName' => __( 'Group name', 'maestro-menu-editor' ), /* translators: Placeholder and accessible label for the field where a new hiding profile's name is typed. */ @@ -346,7 +346,14 @@ public function enqueue() { /* translators: %s: profile name. Accessible label for the button that deletes that hiding profile. */ 'profileDelete' => esc_html__( 'Delete %s', 'maestro-menu-editor' ), /* translators: 1: profile name, 2: number of menu items the profile hides, 3: number of people in the profile. Confirmation before a hiding profile is deleted. */ - 'profileDeleteConfirm' => __( 'Delete “%1$s”? Items it hides: %2$d. People in it: %3$d. Those people will see the items again.', 'maestro-menu-editor' ), + 'profileDeleteConfirm' => __( 'Delete “%1$s”? It hides %2$d items from %3$d people.', 'maestro-menu-editor' ), + /* translators: 1: group name, 2: item count, 3: member count. Complete variants keep singular nouns translatable. */ + 'profileDeleteItem' => __( 'Delete “%1$s”? It hides %2$d item from %3$d people.', 'maestro-menu-editor' ), + /* translators: 1: group name, 2: item count, 3: member count. */ + 'profileDeletePerson' => __( 'Delete “%1$s”? It hides %2$d items from %3$d person.', 'maestro-menu-editor' ), + /* translators: 1: group name, 2: item count, 3: member count. */ + 'profileDeleteBoth' => __( 'Delete “%1$s”? It hides %2$d item from %3$d person.', 'maestro-menu-editor' ), + 'profileDeleteEffect' => __( 'Deleting this group removes its visibility rules. Other rules may still hide these items from its members.', 'maestro-menu-editor' ), /* translators: Caution shown in the hiding-profiles dialog while the editor's own account is among a profile's people. */ 'profileSelfWarning' => __( 'You are in this group, so the items it hides are hidden from your own account too. You can still reach those pages by URL, and the editor stays available.', 'maestro-menu-editor' ), /* translators: Shown when a hiding profile's name is left empty. */ @@ -364,7 +371,7 @@ public function enqueue() { 'previewBtn' => __( 'Preview', 'maestro-menu-editor' ), 'previewUnavailable' => __( 'Role preview is not available to network super admins: WordPress grants them every capability, so their menu cannot be narrowed to a role.', 'maestro-menu-editor' ), 'previewSaveFailed' => __( 'Your latest changes could not be saved, so the preview did not open. Try again once they are saved.', 'maestro-menu-editor' ), - 'confirmAll' => __( 'Reset ALL menu customizations to WordPress defaults? This cannot be undone.', 'maestro-menu-editor' ), + 'confirmAll' => __( 'Reset all menu customizations to WordPress defaults? This cannot be undone.', 'maestro-menu-editor' ), 'drag' => __( 'Drag to reorder', 'maestro-menu-editor' ), /* translators: 1: item title, 2: direction ("up"/"down"), 3: new position number, 4: total items. */ 'moved' => esc_html__( '%1$s moved %2$s, position %3$d of %4$d', 'maestro-menu-editor' ), @@ -407,11 +414,11 @@ public function enqueue() { /* translators: Guided-tour step pointing at the draggable edge of the admin menu. */ 'tourStepWidth' => __( 'Drag the menu\'s edge to make it wider or narrower. When the edge has focus, the arrow keys work too.', 'maestro-menu-editor' ), /* translators: Guided-tour step pointing at the Visibility groups toolbar button. */ - 'tourStepProfiles' => __( 'Visibility groups let you hide a menu item from everyone in a group. Add people to the group, then select it in the menu item\'s Visibility controls.', 'maestro-menu-editor' ), + 'tourStepProfiles' => __( 'Visibility groups let you hide a menu item from everyone in a group. Add people to the group, then select the group in the menu item\'s Visibility settings.', 'maestro-menu-editor' ), 'tourStepPreview' => __( 'Preview as shows the menu a role would see, read-only. Pick a role and press Preview; your changes are saved first.', 'maestro-menu-editor' ), 'tourStepSettings' => __( 'Menu width lets you type an exact width or go back to the default.', 'maestro-menu-editor' ), 'tourStepHelp' => __( 'Want this tour again? Use the help button anytime.', 'maestro-menu-editor' ), - 'tourStep4' => __( 'Your changes save automatically. Reset All returns the whole menu to the WordPress defaults.', 'maestro-menu-editor' ), + 'tourStep4' => __( 'Your changes save automatically. Reset all returns the whole menu to the WordPress defaults.', 'maestro-menu-editor' ), 'tourStep5' => __( 'Click Exit when you are done. Your changes stay live for everyone.', 'maestro-menu-editor' ), /* translators: Accessible label / tooltip for the toolbar button, and title of the dialog, holding menu-wide settings. */ 'settings' => __( 'Menu width', 'maestro-menu-editor' ), diff --git a/languages/maestro-menu-editor.pot b/languages/maestro-menu-editor.pot index 12cc1c2..96b082d 100644 --- a/languages/maestro-menu-editor.pot +++ b/languages/maestro-menu-editor.pot @@ -9,7 +9,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2026-10-23T04:26:45+00:00\n" +"POT-Creation-Date: 2026-10-25T17:03:31+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "X-Generator: WP-CLI 2.12.0\n" "X-Domain: maestro-menu-editor\n" @@ -39,544 +39,583 @@ msgstr "" msgid "https://dan.knauss.ca/" msgstr "" -#: build/maestro-menu-editor/includes/class-admin-bar.php:97 -#: build/maestro-menu-editor/includes/class-admin-bar.php:114 -#: includes/class-admin-bar.php:97 -#: includes/class-admin-bar.php:114 +#: includes/class-admin-bar.php:73 +#: includes/class-preview.php:197 +msgid "Exit preview" +msgstr "" + +#: includes/class-admin-bar.php:75 +msgid "Exit role preview" +msgstr "" + +#: includes/class-admin-bar.php:110 +#: includes/class-admin-bar.php:127 msgid "Exit Menu Editor" msgstr "" -#: build/maestro-menu-editor/includes/class-admin-bar.php:99 -#: includes/class-admin-bar.php:99 +#: includes/class-admin-bar.php:112 msgid "Edit Admin Menu on the Dashboard" msgstr "" -#: build/maestro-menu-editor/includes/class-admin-bar.php:100 -#: includes/class-admin-bar.php:100 +#: includes/class-admin-bar.php:113 msgid "Edit Admin Menu" msgstr "" -#: build/maestro-menu-editor/includes/class-admin-bar.php:115 -#: includes/class-admin-bar.php:115 +#: includes/class-admin-bar.php:128 msgid "Edit Menu" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:249 -#: includes/class-assets.php:259 +#: includes/class-assets.php:273 msgid "Saving…" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:250 -#: includes/class-assets.php:260 +#: includes/class-assets.php:274 msgid "Saved" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:251 -#: includes/class-assets.php:261 +#: includes/class-assets.php:275 msgid "Save failed. Retrying on next change." msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:252 -#: includes/class-assets.php:262 -msgid "Edit Mode" +#: includes/class-assets.php:276 +msgid "Dismiss save error" +msgstr "" + +#: includes/class-assets.php:277 +msgid "Edit mode" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:253 -#: includes/class-assets.php:263 +#: includes/class-assets.php:278 msgid "Menu label" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:254 -#: includes/class-assets.php:264 +#: includes/class-assets.php:279 msgid "Icon" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:255 -#: includes/class-assets.php:265 +#: includes/class-assets.php:280 msgid "Choose an icon" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:256 -#: includes/class-assets.php:266 +#: includes/class-assets.php:281 msgid "Search icons" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:257 -#: includes/class-assets.php:267 +#: includes/class-assets.php:282 msgid "No icon" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:258 -#: includes/class-assets.php:268 +#: includes/class-assets.php:283 msgid "Remove the icon (uses the menu default)." msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:259 -#: includes/class-assets.php:269 +#: includes/class-assets.php:284 msgid "Visibility" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:260 -#: includes/class-assets.php:270 -msgid "Reset Item" +#: includes/class-assets.php:285 +msgid "Reset item" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:261 -#: includes/class-assets.php:271 +#: includes/class-assets.php:286 msgid "Separator" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:262 -#: includes/class-assets.php:272 +#: includes/class-assets.php:287 msgid "Add separator below" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:263 -#: includes/class-assets.php:273 +#: includes/class-assets.php:288 msgid "Remove separator" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:264 -#: includes/class-assets.php:274 +#: includes/class-assets.php:289 msgid "Separator added" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:265 -#: includes/class-assets.php:275 +#: includes/class-assets.php:290 msgid "Separator removed" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:266 -#: includes/class-assets.php:276 -msgid "Reset All" +#: includes/class-assets.php:291 +msgid "Reset all" msgstr "" #. translators: Heading for the role-checkbox group that hides THIS menu item (parent or submenu row). -#: build/maestro-menu-editor/includes/class-assets.php:268 -#: includes/class-assets.php:278 +#: includes/class-assets.php:293 msgid "Hide this item from:" msgstr "" #. translators: Heading for the role-checkbox group that cosmetically hides ALL of a parent's sub-items, independent of the parent's own visibility (COMPAT-10). Shown only for parents that have children. -#: build/maestro-menu-editor/includes/class-assets.php:270 -#: includes/class-assets.php:280 +#: includes/class-assets.php:295 msgid "Hide its sub-items from:" msgstr "" #. translators: %s: role display name. Explains why a "Hide its sub-items from:" checkbox is shown checked and disabled — the parent is already hidden from this role in the "Hide this item from:" group above, so its sub-items are already gone too (WordPress core removes a hidden parent's whole rendered subtree). -#: build/maestro-menu-editor/includes/class-assets.php:272 -#: includes/class-assets.php:282 +#: includes/class-assets.php:297 #, php-format msgid "Already hidden because this item is hidden from %s." msgstr "" +#. translators: Label of the tri-state checkbox at the top of each role group that ticks every role the editing user does not hold (Phase 29). +#: includes/class-assets.php:299 +msgid "All other roles" +msgstr "" + +#. translators: %s: comma-separated names of the editing user's own roles. Shown next to "All other roles" to say which roles it leaves alone. +#: includes/class-assets.php:301 +#, php-format +msgid "not %s" +msgstr "" + +#. translators: %s: the role group's heading, such as "Hide this item from:". Announced after "All other roles" ticks every other role. +#: includes/class-assets.php:303 +#, php-format +msgid "%s every other role is now ticked." +msgstr "" + +#. translators: %s: the role group's heading, such as "Hide this item from:". Announced after "All other roles" clears them. +#: includes/class-assets.php:305 +#, php-format +msgid "%s every other role is now cleared." +msgstr "" + +#. translators: %d: maximum number of roles in one visibility list. Shown when selecting every other role would exceed that limit. +#: includes/class-assets.php:307 +#, php-format +msgid "This would exceed the limit of %d roles per list. Choose roles individually." +msgstr "" + #. translators: Heading for the group that cosmetically hides THIS menu item from named individual people (ROLE-02), as opposed to whole roles. -#: build/maestro-menu-editor/includes/class-assets.php:274 -#: includes/class-assets.php:284 +#: includes/class-assets.php:309 msgid "Hide this item from specific people:" msgstr "" #. translators: Heading for the group that cosmetically hides ALL of a parent's sub-items from named individual people (ROLE-02). Shown only for parents that have children. -#: build/maestro-menu-editor/includes/class-assets.php:276 -#: includes/class-assets.php:286 +#: includes/class-assets.php:311 msgid "Hide its sub-items from specific people:" msgstr "" #. translators: Accessible label and placeholder for the type-ahead field used to find a person to hide a menu item from. -#: build/maestro-menu-editor/includes/class-assets.php:278 -#: includes/class-assets.php:288 +#: includes/class-assets.php:313 msgid "Search for a person" msgstr "" #. translators: Shown in the person picker when a search returns nobody. -#: build/maestro-menu-editor/includes/class-assets.php:280 -#: includes/class-assets.php:290 +#: includes/class-assets.php:315 msgid "No matching people found." msgstr "" #. translators: Shown in the person picker when the search request fails. -#: build/maestro-menu-editor/includes/class-assets.php:282 -#: includes/class-assets.php:292 +#: includes/class-assets.php:317 msgid "Could not search people. Please try again." msgstr "" #. translators: %s: person's display name. Accessible label for the button that removes that person from a hide rule. -#: build/maestro-menu-editor/includes/class-assets.php:284 -#: includes/class-assets.php:294 +#: includes/class-assets.php:319 #, php-format msgid "Remove %s" msgstr "" #. translators: Inline caution shown when an admin adds a hide rule against their OWN account. The rule is still permitted — this only warns. -#: build/maestro-menu-editor/includes/class-assets.php:286 -#: includes/class-assets.php:296 +#: includes/class-assets.php:321 msgid "This hides the item from your own account. You can still reach the page by URL, and the editor stays available." msgstr "" #. translators: %d: maximum number of people allowed per hide list. Shown when the editor tries to add one more than the limit. -#: build/maestro-menu-editor/includes/class-assets.php:288 -#: includes/class-assets.php:298 +#: includes/class-assets.php:323 #, php-format msgid "Limit reached — up to %d people per list. Remove someone to add another." msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:289 +#. translators: Heading for the group of checkboxes, one per hiding profile, that hide THIS menu item from everyone in the ticked profiles (Phase 27). +#: includes/class-assets.php:325 +msgid "Hide this item for visibility groups:" +msgstr "" + +#. translators: Heading for the group of checkboxes, one per hiding profile, that hide ALL of a parent's sub-items from everyone in the ticked profiles. Shown only for parents that have children. +#: includes/class-assets.php:327 +msgid "Hide its sub-items for visibility groups:" +msgstr "" + +#. translators: Shown in the Visibility popover when no hiding profiles exist yet. +#: includes/class-assets.php:329 +msgid "No visibility groups yet." +msgstr "" + +#. translators: Button in the Visibility popover and toolbar label that opens the dialog for creating and editing hiding profiles. +#: includes/class-assets.php:331 +msgid "Manage groups…" +msgstr "" + +#. translators: Title of the dialog for creating and editing hiding profiles (named groups of people that items can be hidden from). +#: includes/class-assets.php:333 +msgid "Visibility groups" +msgstr "" + +#. translators: Explanation at the top of the hiding-profiles dialog. #: includes/class-assets.php:335 -msgid "Reset ALL menu customizations to WordPress defaults? This cannot be undone." +msgid "A visibility group lets you hide a menu item from everyone in the group. Add people to the group, then select the group in the menu item's Visibility settings. This hides the menu link without changing page access." +msgstr "" + +#. translators: Persistent next-step guidance beside each saved hiding profile. +#: includes/class-assets.php:337 +msgid "Add people below, then select this group in a menu item's Visibility settings. Changes save automatically." msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:290 -#: includes/class-assets.php:336 +#. translators: Accessible label for the text field that renames a hiding profile. +#: includes/class-assets.php:339 +msgid "Group name" +msgstr "" + +#. translators: Placeholder and accessible label for the field where a new hiding profile's name is typed. +#: includes/class-assets.php:341 +msgid "New group name" +msgstr "" + +#. translators: Button that creates a hiding profile with the name typed beside it. +#: includes/class-assets.php:343 +msgid "Add group" +msgstr "" + +#. translators: %s: profile name. Heading for the list of people in that hiding profile. +#: includes/class-assets.php:345 +#, php-format +msgid "People in %s:" +msgstr "" + +#. translators: %s: profile name. Accessible label for the button that deletes that hiding profile. +#: includes/class-assets.php:347 +#, php-format +msgid "Delete %s" +msgstr "" + +#. translators: 1: profile name, 2: number of menu items the profile hides, 3: number of people in the profile. Confirmation before a hiding profile is deleted. +#: includes/class-assets.php:349 +#, php-format +msgid "Delete “%1$s”? It hides %2$d items from %3$d people." +msgstr "" + +#. translators: 1: group name, 2: item count, 3: member count. Complete variants keep singular nouns translatable. +#: includes/class-assets.php:351 +#, php-format +msgid "Delete “%1$s”? It hides %2$d item from %3$d people." +msgstr "" + +#. translators: 1: group name, 2: item count, 3: member count. +#: includes/class-assets.php:353 +#, php-format +msgid "Delete “%1$s”? It hides %2$d items from %3$d person." +msgstr "" + +#. translators: 1: group name, 2: item count, 3: member count. +#: includes/class-assets.php:355 +#, php-format +msgid "Delete “%1$s”? It hides %2$d item from %3$d person." +msgstr "" + +#: includes/class-assets.php:356 +msgid "Deleting this group removes its visibility rules. Other rules may still hide these items from its members." +msgstr "" + +#. translators: Caution shown in the hiding-profiles dialog while the editor's own account is among a profile's people. +#: includes/class-assets.php:358 +msgid "You are in this group, so the items it hides are hidden from your own account too. You can still reach those pages by URL, and the editor stays available." +msgstr "" + +#. translators: Shown when a hiding profile's name is left empty. +#: includes/class-assets.php:360 +msgid "Enter a name for the group." +msgstr "" + +#. translators: Shown when a hiding profile is given a name another profile already uses (names are compared ignoring case). +#: includes/class-assets.php:362 +msgid "Another group already has that name." +msgstr "" + +#. translators: %d: maximum number of hiding profiles. Shown when the limit is reached. +#: includes/class-assets.php:364 +#, php-format +msgid "Limit reached — up to %d groups." +msgstr "" + +#. translators: Screen-reader announcement after a hiding profile is created. +#: includes/class-assets.php:366 +msgid "Group added" +msgstr "" + +#. translators: Screen-reader announcement after a hiding profile is deleted. +#: includes/class-assets.php:368 +msgid "Group deleted" +msgstr "" + +#. translators: Label for the role picker that previews the menu as another role. +#: includes/class-assets.php:370 +#: includes/class-preview.php:189 +msgid "Preview as" +msgstr "" + +#: includes/class-assets.php:371 +#: includes/class-preview.php:196 +msgid "Preview" +msgstr "" + +#: includes/class-assets.php:372 +msgid "Role preview is not available to network super admins: WordPress grants them every capability, so their menu cannot be narrowed to a role." +msgstr "" + +#: includes/class-assets.php:373 +msgid "Your latest changes could not be saved, so the preview did not open. Try again once they are saved." +msgstr "" + +#: includes/class-assets.php:374 +msgid "Reset all menu customizations to WordPress defaults? This cannot be undone." +msgstr "" + +#: includes/class-assets.php:375 msgid "Drag to reorder" msgstr "" #. translators: 1: item title, 2: direction ("up"/"down"), 3: new position number, 4: total items. -#: build/maestro-menu-editor/includes/class-assets.php:292 -#: includes/class-assets.php:338 +#: includes/class-assets.php:377 #, php-format msgid "%1$s moved %2$s, position %3$d of %4$d" msgstr "" #. translators: %s: item title. -#: build/maestro-menu-editor/includes/class-assets.php:294 -#: includes/class-assets.php:340 +#: includes/class-assets.php:379 #, php-format msgid "%s is already first" msgstr "" #. translators: %s: item title. -#: build/maestro-menu-editor/includes/class-assets.php:296 -#: includes/class-assets.php:342 +#: includes/class-assets.php:381 #, php-format msgid "%s is already last" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:297 -#: includes/class-assets.php:343 +#: includes/class-assets.php:382 msgid "up" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:298 -#: includes/class-assets.php:344 +#: includes/class-assets.php:383 msgid "down" msgstr "" #. translators: Accessible label for the "move selected item up" button in the editor panel. -#: build/maestro-menu-editor/includes/class-assets.php:300 -#: includes/class-assets.php:346 +#: includes/class-assets.php:385 msgid "Move up" msgstr "" #. translators: Accessible label for the "move selected item down" button in the editor panel. -#: build/maestro-menu-editor/includes/class-assets.php:302 -#: includes/class-assets.php:348 +#: includes/class-assets.php:387 msgid "Move down" msgstr "" #. translators: Short label appended to modified menu items for screen readers. -#: build/maestro-menu-editor/includes/class-assets.php:304 -#: includes/class-assets.php:350 +#: includes/class-assets.php:389 msgid "(modified)" msgstr "" #. translators: One-time hint shown to first-time users of the menu editor. -#: build/maestro-menu-editor/includes/class-assets.php:306 -#: includes/class-assets.php:352 +#: includes/class-assets.php:391 msgid "Click a menu item to start editing." msgstr "" #. translators: Label for the button that dismisses the first-run hint. -#: build/maestro-menu-editor/includes/class-assets.php:308 -#: includes/class-assets.php:354 +#: includes/class-assets.php:393 msgid "Got it" msgstr "" #. translators: Accessible label / tooltip for the toolbar button that replays the guided tour. -#: build/maestro-menu-editor/includes/class-assets.php:310 -#: includes/class-assets.php:356 +#: includes/class-assets.php:395 msgid "Show me around" msgstr "" #. translators: Title of the guided-tour tooltip dialog. -#: build/maestro-menu-editor/includes/class-assets.php:312 -#: includes/class-assets.php:358 +#: includes/class-assets.php:397 msgid "Editing the menu" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:313 -#: includes/class-assets.php:359 +#: includes/class-assets.php:398 msgid "Next" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:314 -#: includes/class-assets.php:360 +#: includes/class-assets.php:399 msgid "Back" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:315 -#: includes/class-assets.php:361 +#: includes/class-assets.php:400 msgid "Done" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:316 -#: includes/class-assets.php:362 -msgid "Skip" +#: includes/class-assets.php:401 +msgid "Close dialog" +msgstr "" + +#: includes/class-assets.php:402 +msgid "Close tour" msgstr "" #. translators: 1: current step number, 2: total steps. e.g. "2 of 5". -#: build/maestro-menu-editor/includes/class-assets.php:318 -#: includes/class-assets.php:364 +#: includes/class-assets.php:404 #, php-format msgid "%1$d of %2$d" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:319 -#: includes/class-assets.php:367 +#: includes/class-assets.php:407 msgid "Click any menu item to edit it right here on the menu." msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:320 -msgid "Rename it, change its icon, or hide it from roles using these controls." +#: includes/class-assets.php:408 +msgid "Type a new name for the selected item here." msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:321 -#: includes/class-assets.php:369 +#: includes/class-assets.php:409 msgid "Reorder items by dragging — or use the up and down arrows." msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:322 -msgid "Your changes save automatically. Reset one item, or Reset All to start over." +#: includes/class-assets.php:410 +msgid "Change the item's icon. Pick one from Dashicons or Bootstrap, or choose no icon." msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:323 -#: includes/class-assets.php:379 -msgid "Click Exit when you are done. Your changes stay live for everyone." +#: includes/class-assets.php:411 +msgid "Hide the item from chosen roles or people. Everyone else still sees it." msgstr "" -#. translators: Accessible label / tooltip for the toolbar button, and title of the dialog, holding menu-wide settings. -#: build/maestro-menu-editor/includes/class-assets.php:325 -#: includes/class-assets.php:381 -msgid "Menu settings" +#: includes/class-assets.php:412 +msgid "Undo every change to just this item." msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:326 -#: includes/class-assets.php:382 +#: includes/class-assets.php:413 +msgid "Add a separator line below this item. To remove one, select the separator and use the trash button." +msgstr "" + +#. translators: Guided-tour step pointing at the draggable edge of the admin menu. +#: includes/class-assets.php:415 +msgid "Drag the menu's edge to make it wider or narrower. When the edge has focus, the arrow keys work too." +msgstr "" + +#. translators: Guided-tour step pointing at the Visibility groups toolbar button. +#: includes/class-assets.php:417 +msgid "Visibility groups let you hide a menu item from everyone in a group. Add people to the group, then select the group in the menu item's Visibility settings." +msgstr "" + +#: includes/class-assets.php:418 +msgid "Preview as shows the menu a role would see, read-only. Pick a role and press Preview; your changes are saved first." +msgstr "" + +#: includes/class-assets.php:419 +msgid "Menu width lets you type an exact width or go back to the default." +msgstr "" + +#: includes/class-assets.php:420 +msgid "Want this tour again? Use the help button anytime." +msgstr "" + +#: includes/class-assets.php:421 +msgid "Your changes save automatically. Reset all returns the whole menu to the WordPress defaults." +msgstr "" + +#: includes/class-assets.php:422 +msgid "Click Exit when you are done. Your changes stay live for everyone." +msgstr "" + +#. translators: Accessible label / tooltip for the toolbar button, and title of the dialog, holding menu-wide settings. +#: includes/class-assets.php:424 +#: includes/class-assets.php:425 msgid "Menu width" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:327 -#: includes/class-assets.php:383 +#: includes/class-assets.php:426 msgid "Menu width in pixels" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:328 -#: includes/class-assets.php:384 +#: includes/class-assets.php:427 msgid "Widen the menu so longer labels fit on one line. Applies on every admin page, for everyone, on screens 961px and wider." msgstr "" #. translators: %d: WordPress's default admin menu width in pixels. -#: build/maestro-menu-editor/includes/class-assets.php:330 -#: includes/class-assets.php:386 +#: includes/class-assets.php:429 #, php-format msgid "Using the WordPress default (%dpx)." msgstr "" #. translators: %d: the stored admin menu width in pixels. -#: build/maestro-menu-editor/includes/class-assets.php:332 -#: includes/class-assets.php:388 +#: includes/class-assets.php:431 #, php-format msgid "Custom width: %dpx." msgstr "" #. translators: %d: the width in pixels another plugin is holding the admin menu at. -#: build/maestro-menu-editor/includes/class-assets.php:334 -#: includes/class-assets.php:390 +#: includes/class-assets.php:433 #, php-format msgid "Another plugin sets the menu width (%dpx), so this setting has no effect while that plugin is active." msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:335 -#: includes/class-assets.php:391 +#: includes/class-assets.php:434 msgid "Use default" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:336 -#: includes/class-assets.php:392 +#: includes/class-assets.php:435 msgid "Menu width reset to the WordPress default." msgstr "" #. translators: Accessible label / tooltip for the handle on the admin menu's edge that resizes it. -#: build/maestro-menu-editor/includes/class-assets.php:338 -#: includes/class-assets.php:394 +#: includes/class-assets.php:437 msgid "Resize menu" msgstr "" #. translators: %d: a width in pixels, read out by screen readers. -#: build/maestro-menu-editor/includes/class-assets.php:340 -#: includes/class-assets.php:396 +#: includes/class-assets.php:439 #, php-format msgid "%d pixels" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:370 -#: includes/class-assets.php:511 +#: includes/class-assets.php:554 msgid "Dashicons" msgstr "" -#: build/maestro-menu-editor/includes/class-assets.php:385 -#: includes/class-assets.php:526 +#: includes/class-assets.php:569 msgid "Bootstrap" msgstr "" #. translators: %s: name of another admin-menu editor plugin, e.g. "Admin Menu Editor". -#: build/maestro-menu-editor/includes/class-conflict-notice.php:62 #: includes/class-conflict-notice.php:62 #, php-format msgid "%s is also active. It rebuilds the admin menu too, so its settings can override or hide the changes you make here." msgstr "" -#: build/maestro-menu-editor/includes/class-rest.php:113 -#: includes/class-rest.php:113 -msgid "Invalid config payload." -msgstr "" - -#. translators: Heading for the group of checkboxes, one per hiding profile, that hide THIS menu item from everyone in the ticked profiles (Phase 27). -#: includes/class-assets.php:300 -msgid "Hide this item for visibility groups:" -msgstr "" - -#. translators: Heading for the group of checkboxes, one per hiding profile, that hide ALL of a parent's sub-items from everyone in the ticked profiles. Shown only for parents that have children. -#: includes/class-assets.php:302 -msgid "Hide its sub-items for visibility groups:" -msgstr "" - -#. translators: Shown in the Visibility popover when no hiding profiles exist yet. -#: includes/class-assets.php:304 -msgid "No visibility groups yet." -msgstr "" - -#. translators: Button in the Visibility popover and toolbar label that opens the dialog for creating and editing hiding profiles. -#: includes/class-assets.php:306 -msgid "Manage groups…" -msgstr "" - -#. translators: Title of the dialog for creating and editing hiding profiles (named groups of people that items can be hidden from). -#: includes/class-assets.php:308 -msgid "Visibility groups" -msgstr "" - -#. translators: Explanation at the top of the hiding-profiles dialog. -#: includes/class-assets.php:310 -msgid "A visibility group lets you hide a menu item from everyone in the group. Add people to the group, then select it in the menu item's Visibility controls. This hides the menu link without changing page access." -msgstr "" - -#. translators: Accessible label for the text field that renames a hiding profile. -#: includes/class-assets.php:312 -msgid "Group name" -msgstr "" - -#. translators: Placeholder and accessible label for the field where a new hiding profile's name is typed. -#: includes/class-assets.php:314 -msgid "New group name" -msgstr "" - -#. translators: Button that creates a hiding profile with the name typed beside it. -#: includes/class-assets.php:316 -msgid "Add group" -msgstr "" - -#. translators: %s: profile name. Heading for the list of people in that hiding profile. -#: includes/class-assets.php:318 -#, php-format -msgid "People in %s:" -msgstr "" - -#. translators: %s: profile name. Accessible label for the button that deletes that hiding profile. -#: includes/class-assets.php:320 -#, php-format -msgid "Delete %s" -msgstr "" - -#. translators: 1: profile name, 2: number of menu items the profile hides, 3: number of people in the profile. Confirmation before a hiding profile is deleted. -#: includes/class-assets.php:322 +#. translators: %s: role name, such as "Editor". +#: includes/class-preview.php:184 #, php-format -msgid "Delete “%1$s”? Items it hides: %2$d. People in it: %3$d. Those people will see the items again." +msgid "Previewing menu as %s — read-only" msgstr "" -#. translators: Caution shown in the hiding-profiles dialog while the editor's own account is among a profile's people. -#: includes/class-assets.php:324 -msgid "You are in this group, so the items it hides are hidden from your own account too. You can still reach those pages by URL, and the editor stays available." +#: includes/class-preview.php:187 +msgid "The menu you see now is a representation of how it should appear to users in the role you’ve selected. This preview is based on the rules WordPress and your plugins build for this role, with Maestro’s rules applied last. Keep in mind it’s an approximation: rules for specific users or visibility groups, capabilities given to individual users, people with more than one role, and plugins that check the user directly can create different, unexpected results. Clicking a menu item still opens the page with your own permissions." msgstr "" -#. translators: Shown when a hiding profile's name is left empty. -#: includes/class-assets.php:326 -msgid "Enter a name for the group." +#: includes/class-preview.php:211 +msgid "That role preview link has expired. Choose the role again from the menu editor." msgstr "" -#. translators: Shown when a hiding profile is given a name another profile already uses (names are compared ignoring case). -#: includes/class-assets.php:328 -msgid "Another group already has that name." +#: includes/class-preview.php:212 +msgid "That role cannot be previewed on this site." msgstr "" -#. translators: %d: maximum number of hiding profiles. Shown when the limit is reached. -#: includes/class-assets.php:330 -#, php-format -msgid "Limit reached — up to %d groups." +#: includes/class-preview.php:213 +msgid "That role has no access to the WordPress admin, so there is no menu to preview." msgstr "" -#. translators: Screen-reader announcement after a hiding profile is created. -#: includes/class-assets.php:332 -msgid "Group added" +#: includes/class-preview.php:214 +msgid "Role preview is not available to network super admins. Use a site administrator account to preview this site’s roles." msgstr "" -#. translators: Screen-reader announcement after a hiding profile is deleted. -#: includes/class-assets.php:334 -msgid "Group deleted" +#: includes/class-preview.php:215 +msgid "Role preview runs on the Dashboard. Choose the role again from the menu editor." msgstr "" -#: includes/class-assets.php:368 -msgid "Type a new name for the selected item here." -msgstr "" - -#: includes/class-assets.php:370 -msgid "Change the item's icon. Pick one from Dashicons or Bootstrap, or choose no icon." -msgstr "" - -#: includes/class-assets.php:371 -msgid "Hide the item from chosen roles or people. Everyone else still sees it." -msgstr "" - -#: includes/class-assets.php:372 -msgid "Undo every change to just this item." -msgstr "" - -#: includes/class-assets.php:373 -msgid "Add a separator line below this item. To remove one, select the separator and use the trash button." -msgstr "" - -#. translators: Guided-tour step pointing at the draggable edge of the admin menu. -#: includes/class-assets.php:375 -msgid "Drag the menu's edge to make it wider or narrower. When the edge has focus, the arrow keys work too." -msgstr "" - -#: includes/class-assets.php:376 -msgid "Menu settings let you type an exact menu width or go back to the default." -msgstr "" - -#: includes/class-assets.php:377 -msgid "Want this tour again? Use the help button anytime." -msgstr "" - -#: includes/class-assets.php:378 -msgid "Your changes save automatically. Reset All returns the whole menu to the WordPress defaults." -msgstr "" - -#. translators: Persistent next-step guidance beside each saved hiding profile. -#: includes/class-assets.php:327 -msgid "Add people below, then select this group in a menu item's Visibility controls. Changes save automatically." -msgstr "" - -#. translators: Guided-tour step pointing at the Visibility groups toolbar button. -#: includes/class-assets.php:394 -msgid "Visibility groups let you hide a menu item from everyone in a group. Add people to the group, then select it in the menu item's Visibility controls." +#: includes/class-rest.php:113 +msgid "Invalid config payload." msgstr "" diff --git a/tests/e2e/dialog-standard.spec.ts b/tests/e2e/dialog-standard.spec.ts new file mode 100644 index 0000000..333805b --- /dev/null +++ b/tests/e2e/dialog-standard.spec.ts @@ -0,0 +1,98 @@ +import { test, expect } from './fixtures'; + +for ( const viewport of [ { width: 375, height: 812 }, { width: 844, height: 390 }, { width: 1280, height: 900 } ] ) { + test.describe( `dialog standard ${ viewport.width }`, () => { + test.use( { viewport, hasTouch: true } ); + for ( const kind of ( viewport.width >= 961 ? [ 'icon', 'vis', 'profiles', 'preview', 'settings', 'tour' ] : [ 'icon', 'vis', 'profiles', 'preview' ] ) ) { + test( `${ kind } shares button geometry and switches focus indicators with input`, async ( { page } ) => { + await page.goto( '/wp-admin/index.php?maestro_edit=1' ); + await page.locator( '#menu-posts > a.menu-top' ).tap(); + const trigger = kind === 'preview' ? '.maestro-preview-toggle' : kind === 'tour' ? '.maestro-tour-help' : `.maestro-${ kind }-btn`; + await page.locator( trigger ).tap(); + const dialog = page.locator( '.maestro-popover, .maestro-tour' ); + await expect( dialog ).toBeVisible(); + const buttons = dialog.locator( 'button:visible, input[type="text"]:visible, input[type="search"]:visible, input[type="number"]:visible, select:visible' ); + for ( const button of await buttons.all() ) { + await expect( button ).toHaveCSS( 'border-top-left-radius', '3px' ); + } + // The focused close control/container stays focused without a pointer ring. + expect( await dialog.evaluate( d => d.contains( document.activeElement ) || d === document.activeElement ) ).toBe( true ); + await expect( page.locator( ':focus' ) ).toHaveCSS( 'outline-style', 'none' ); + await page.keyboard.press( 'Tab' ); + const focused = page.locator( ':focus' ); + expect( await dialog.evaluate( d => d.contains( document.activeElement ) ) ).toBe( true ); + await expect( focused ).toHaveCSS( 'outline-style', 'solid' ); + await expect( focused ).toHaveCSS( 'outline-width', '2px' ); + await expect( focused ).toHaveCSS( 'box-shadow', 'none' ); + } ); + } + } ); +} + +for ( const viewport of [ { width: 375, height: 812 }, { width: 844, height: 390 }, { width: 1280, height: 900 } ] ) { + test.describe( `tool switching ${ viewport.width }`, () => { + test.use( { viewport, hasTouch: true } ); + test( 'switching tools replaces the dialog and preserves icon colours', async ( { page } ) => { + await page.goto( '/wp-admin/index.php?maestro_edit=1' ); + await page.locator( '#menu-posts > a.menu-top' ).tap(); + await expect( page.locator( '.maestro-preview-toggle .dashicons' ) ).toHaveClass( /dashicons-welcome-view-site/ ); + await expect( page.locator( '.maestro-vis-btn .dashicons' ) ).toHaveClass( /dashicons-visibility/ ); + const tools = [ '.maestro-preview-toggle', '.maestro-profiles-btn', '.maestro-icon-btn', '.maestro-vis-btn', ...( viewport.width >= 961 ? [ '.maestro-tour-help' ] : [] ), '.maestro-preview-toggle' ]; + const classes = [ 'maestro-preview-popover', 'maestro-profiles-popover', 'maestro-icon-popover', 'maestro-vis-popover', ...( viewport.width >= 961 ? [ 'maestro-tour' ] : [] ), 'maestro-preview-popover' ]; + for ( let i = 0; i < tools.length; i++ ) { + const trigger = page.locator( tools[i] ); + const colour = await trigger.locator( '.dashicons' ).evaluate( el => getComputedStyle( el ).color ); + await trigger.tap(); + await expect( page.locator( '.maestro-popover, .maestro-tour' ) ).toHaveCount( 1 ); + await expect( page.locator( '.' + classes[i] ) ).toBeVisible(); + await expect( trigger ).toHaveAttribute( 'aria-expanded', 'true' ); + await expect( page.locator( '.maestro-toolbar [aria-expanded="true"]' ) ).toHaveCount( 1 ); + await expect( trigger.locator( '.dashicons' ) ).toHaveCSS( 'color', colour ); + await expect( trigger ).not.toHaveCSS( 'background-color', 'rgba(0, 0, 0, 0)' ); + } + await page.getByRole( 'button', { name: 'Close dialog', exact: true } ).tap(); + await expect( page.locator( '.maestro-toolbar [aria-expanded="true"]' ) ).toHaveCount( 0 ); + } ); + } ); +} + +for ( const viewport of [ { width: 320, height: 568 }, { width: 375, height: 812 }, { width: 667, height: 375 }, { width: 844, height: 390 } ] ) { + test.describe( `bounded toolbar ${ viewport.width }`, () => { + test.use( { viewport, hasTouch: true } ); + test( 'keeps editing space and touch targets while every remaining tool stays reachable', async ( { page } ) => { + await page.goto( '/wp-admin/index.php?maestro_edit=1' ); + await page.locator( '#menu-posts > a.menu-top' ).tap(); + const toolbar = page.locator( '.maestro-toolbar' ); + expect( ( await toolbar.boundingBox() )!.height ).toBeLessThanOrEqual( viewport.width > viewport.height ? 70 : 120 ); + await expect( toolbar.locator( '.maestro-tour-help' ) ).toBeHidden(); + for ( const selector of [ '.maestro-icon-btn', '.maestro-vis-btn', '.maestro-profiles-btn', '.maestro-preview-toggle' ] ) { + const button = toolbar.locator( selector ); + await button.scrollIntoViewIfNeeded(); + const box = ( await button.boundingBox() )!; + expect( box.width ).toBeGreaterThanOrEqual( 44 ); + expect( box.height ).toBeGreaterThanOrEqual( 44 ); + await button.tap(); + const dialog = page.locator( '.maestro-mobile-sheet' ); + await expect( dialog ).toBeVisible(); + const sheet = ( await dialog.boundingBox() )!; + expect( sheet.y + sheet.height ).toBeLessThanOrEqual( ( await toolbar.boundingBox() )!.y ); + await dialog.getByRole( 'button', { name: 'Close dialog', exact: true } ).tap(); + } + } ); + } ); +} + +test.describe( 'group deletion copy', () => { + test.use( { viewport: { width: 375, height: 812 }, hasTouch: true } ); + test( 'empty groups have a concise count-only confirmation', async ( { page } ) => { + await page.goto( '/wp-admin/index.php?maestro_edit=1' ); + await page.locator( '.maestro-profiles-btn' ).tap(); + await expect( page.locator( '.maestro-profiles-intro' ) ).toContainText( 'Visibility settings' ); + await page.getByRole( 'textbox', { name: 'New group name' } ).fill( 'Empty group' ); + await page.getByRole( 'button', { name: 'Add group', exact: true } ).tap(); + let message = ''; + page.once( 'dialog', async dialog => { message = dialog.message(); await dialog.dismiss(); } ); + await page.getByRole( 'button', { name: 'Delete Empty group', exact: true } ).tap(); + expect( message ).toBe( 'Delete “Empty group”? It hides 0 items from 0 people.' ); + } ); +} ); diff --git a/tests/e2e/mobile-usability.spec.ts b/tests/e2e/mobile-usability.spec.ts index 2029ada..85dc075 100644 --- a/tests/e2e/mobile-usability.spec.ts +++ b/tests/e2e/mobile-usability.spec.ts @@ -74,7 +74,9 @@ test( 'group sheet follows visual viewport and keeps focused input above keyboar await expect( pop ).toHaveCount( 0 ); } ); -test( 'touch tour keeps the menu open and covers all phone controls', async ( { page } ) => { +test( 'coach tips are omitted on phones and remain available on larger screens', async ( { page } ) => { + await expect( page.locator( '.maestro-tour-help' ) ).toBeHidden(); + await page.setViewportSize( { width: 900, height: 900 } ); await page.locator( '.maestro-tour-help' ).tap(); const texts: string[] = []; while ( await page.locator( '.maestro-tour' ).count() ) { @@ -84,7 +86,7 @@ test( 'touch tour keeps the menu open and covers all phone controls', async ( { await expect( page.locator( '#adminmenu' ) ).toBeVisible(); } expect( texts ).toHaveLength( 12 ); - for ( const label of [ 'Type a new name', 'Reorder', 'icon', 'Hide the item', 'Visibility groups', 'Preview as', 'Reset All', 'Exit' ] ) { + for ( const label of [ 'Type a new name', 'Reorder', 'icon', 'Hide the item', 'Visibility groups', 'Preview as', 'Reset all', 'Exit' ] ) { expect( texts.some( text => text.includes( label ) ), label ).toBe( true ); } expect( texts.some( text => text.includes( 'Menu settings' ) ) ).toBe( false ); diff --git a/tests/e2e/specs/phone-layout.spec.ts b/tests/e2e/specs/phone-layout.spec.ts index bb59a78..a10f255 100644 --- a/tests/e2e/specs/phone-layout.spec.ts +++ b/tests/e2e/specs/phone-layout.spec.ts @@ -29,53 +29,13 @@ const LANDSCAPE_WIDE = { width: 844, height: 390 }; const menuOpen = ( page: Page ) => page.evaluate( () => document.getElementById( 'wpwrap' )!.classList.contains( 'wp-responsive-open' ) ); -// Every step but the width handle's and Settings', which only render at -// 961px and up: the menu width they set does not apply below that. -// The profiles tip is matched by either name, so this spec survives the -// Visibility groups rename (#250) in either merge order. -const PHONE_STEPS: ( string | RegExp )[] = [ - 'Click any menu item', - 'Type a new name', - 'Reorder items', - 'icon', - 'Hide the item', - 'Undo every change', - 'separator', - /Hiding profiles|Visibility groups/, - 'Preview as', - 'tour again', - 'Reset All', - 'Exit', -]; - for ( const [ name, viewport ] of [ [ 'portrait', PORTRAIT ], [ 'landscape', LANDSCAPE ], [ 'wide landscape', LANDSCAPE_WIDE ] ] as const ) { test.describe( `phone, ${ name } (${ viewport.width }x${ viewport.height })`, () => { test.use( { viewport } ); - test( 'the tour shows every step, numbered without gaps, and leaves the menu open', async ( { page } ) => { + test( 'the phone toolbar omits coach tips', async ( { page } ) => { await page.goto( '/wp-admin/index.php?maestro_edit=1' ); - // From the toolbar's "?", which is itself a tap outside the menu - // list, rather than the first-run launch. - await page.locator( '.maestro-toolbar .maestro-tour-help' ).click(); - await expect( page.locator( '.maestro-tour' ) ).toBeVisible(); - - const progress = page.locator( '.maestro-tour-progress' ); - for ( const [ i, text ] of PHONE_STEPS.entries() ) { - await expect( progress ).toHaveText( `${ i + 1 } of ${ PHONE_STEPS.length }` ); - await expect( page.locator( '.maestro-tour-text' ) ).toContainText( text ); - // The coachmark stays on screen, not under the toolbar or past an edge. - const box = await page.locator( '.maestro-tour' ).boundingBox(); - expect( box!.y ).toBeGreaterThanOrEqual( 0 ); - expect( box!.y + box!.height ).toBeLessThanOrEqual( viewport.height ); - await page.locator( '.maestro-tour-next' ).click(); - } - await expect( page.locator( '.maestro-tour' ) ).toHaveCount( 0 ); - - // Done is a click outside nothing: the editor is still on screen. - await expect( page.locator( '.maestro-toolbar' ) ).toBeVisible(); - if ( viewport.width <= 782 ) { - expect( await menuOpen( page ) ).toBe( true ); - } + await expect( page.locator( '.maestro-tour-help' ) ).toBeHidden(); } ); test( 'the toolbar is compact and fits the screen', async ( { page } ) => { @@ -85,7 +45,7 @@ for ( const [ name, viewport ] of [ [ 'portrait', PORTRAIT ], [ 'landscape', LAN const m = await page.evaluate( () => { const bar = document.querySelector( '.maestro-toolbar' )!.getBoundingClientRect(); - const controls = Array.from( document.querySelectorAll( '.maestro-toolbar button, .maestro-toolbar input' ) ) + const controls = Array.from( document.querySelectorAll( '.maestro-toolbar .maestro-panel, .maestro-toolbar .maestro-toolbar-right' ) ) .filter( ( n ) => ( n as HTMLElement ).offsetParent !== null ) .map( ( n ) => n.getBoundingClientRect() ); return { @@ -100,7 +60,7 @@ for ( const [ name, viewport ] of [ [ 'portrait', PORTRAIT ], [ 'landscape', LAN // Upright: two rows of 44px touch targets. On its side: one. const rows = viewport.width < viewport.height ? 2 : 1; - expect( m.height ).toBeLessThanOrEqual( rows * 44 + ( rows - 1 ) * 4 + 16 ); + expect( m.height ).toBeLessThanOrEqual( rows * 48 + ( rows - 1 ) * 4 + 16 ); expect( m.left ).toBeGreaterThanOrEqual( 0 ); expect( m.right ).toBeLessThanOrEqual( m.clientWidth ); expect( m.bottom ).toBeLessThanOrEqual( viewport.height ); diff --git a/tests/e2e/tour.spec.ts b/tests/e2e/tour.spec.ts index b72f168..dad0497 100644 --- a/tests/e2e/tour.spec.ts +++ b/tests/e2e/tour.spec.ts @@ -36,7 +36,7 @@ test( 'guided tour: auto-launch, step through, seen-flag, replay, Esc', async ( 'Preview as', 'Menu width', 'tour again', - 'Reset All', + 'Reset all', 'Exit', ]; await expect( page.locator( '.maestro-tour-progress' ) ).toHaveText( `1 of ${ expected.length }` ); diff --git a/tests/js/profile-delete-message.test.mjs b/tests/js/profile-delete-message.test.mjs new file mode 100644 index 0000000..cf83696 --- /dev/null +++ b/tests/js/profile-delete-message.test.mjs @@ -0,0 +1,29 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { createRequire } from 'node:module'; +const { profileDeleteMessage } = createRequire(import.meta.url)('../../assets/maestro-logic.js'); +const strings = { + profileDeleteConfirm: 'Delete “%1$s”? It hides %2$d items from %3$d people.', + profileDeleteItem: 'Delete “%1$s”? It hides %2$d item from %3$d people.', + profileDeletePerson: 'Delete “%1$s”? It hides %2$d items from %3$d person.', + profileDeleteBoth: 'Delete “%1$s”? It hides %2$d item from %3$d person.', + profileDeleteEffect: 'Deleting this group removes its visibility rules. Other rules may still hide these items from its members.', +}; +for (const [items, people] of [[0, 0], [0, 2], [3, 0], [3, 2], [1, 0], [0, 1], [1, 1], [1, 2], [2, 1]]) { + test(`deleting a group with ${items} items and ${people} people describes only applicable effects`, () => { + const message = profileDeleteMessage('$& group', items, people, strings); + assert.equal(message, `Delete “$& group”? It hides ${items} ${items === 1 ? 'item' : 'items'} from ${people} ${people === 1 ? 'person' : 'people'}.` + + (items && people ? ' ' + strings.profileDeleteEffect : '')); + assert.doesNotMatch(message, /will see/); + }); +} + +test('shared action labels use sentence case', async () => { + const { readFile } = await import('node:fs/promises'); + const php = await readFile(new URL('../../includes/class-assets.php', import.meta.url), 'utf8'); + for (const key of ['modeLabel', 'resetItem', 'resetAll', 'profileAdd', 'widthUseDefault', 'closeDialog', 'closeTour', 'iconDialog', 'profiles', 'previewAs', 'settings']) { + const match = php.match(new RegExp("'" + key + "'\\s*=>\\s*__\\( '([^']+)'")); + assert.ok(match, `missing shared label ${key}`); + assert.equal(match[1].slice(1), match[1].slice(1).toLowerCase(), `${key} must use sentence case`); + } +}); From 9392fb702e118ce84945c0d27669c0ebc075aa00 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 25 Oct 2026 21:35:27 +0000 Subject: [PATCH 3/3] Update two E2E checks for #265's phone toolbar, add its fixes to 1.9.0 #265 makes each phone tool group scroll sideways and keeps the open tool highlighted, so two older checks failed on purpose-built behavior: - menu-width's narrow-toolbar test measured every button's right edge, including ones scrolled out of a group. It now measures the groups that clip them and checks that the page itself does not scroll sideways. - the touch-hover test read the Visibility button while its popover was open. It now closes the popover with a tap outside, so it still checks that a tap leaves no hover accent behind. The 1.9.0 changelog gains the dialog, group-deletion, tool-switching and icon fixes, and the readme and user guide call Preview as the screen button. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012tY8BSUE4tF9wo8m1hx4qW --- docs/user-guide.md | 2 +- readme.txt | 10 +++++++--- tests/e2e/desktop-refinements.spec.ts | 7 +++++++ tests/e2e/specs/menu-width.spec.ts | 10 ++++++++-- 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/docs/user-guide.md b/docs/user-guide.md index f81b4bd..0457fff 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -83,7 +83,7 @@ the same control. ## Preview The Menu As A Role -To check what a role still sees, open **Preview as** using the eye button in the +To check what a role still sees, open **Preview as** using the screen button in the editor toolbar, choose a role, then press **Preview**. Changing the selection alone does not start a preview. Maestro saves anything still pending first; if the save fails, the preview does not open. diff --git a/readme.txt b/readme.txt index 8681f0e..7d0e9bc 100644 --- a/readme.txt +++ b/readme.txt @@ -76,7 +76,7 @@ named individuals, so what each person sees can differ. The rename/reorder/icon = Can I see the menu the way an Editor would? = -Yes. Press **Preview as** (the eye button in the editor toolbar), choose a +Yes. Press **Preview as** (the screen button in the editor toolbar), choose a role and press **Preview**. The Dashboard shows that role's menu, with your rules for the role applied, under a read-only banner with **Exit preview**. You stay signed in as yourself and nothing is saved. It is an approximation: rules for specific @@ -210,7 +210,7 @@ If Maestro saves you time or brings you or your clients the joy of a tidy admin = 1.9.0 = 1.8.0 was never released on its own, so this release carries its changes too. -* **New: preview the menu as a role.** Press **Preview as** (the eye button in the editing toolbar), choose a role and press **Preview**. The Dashboard then shows that role's menu with your rules applied, under a read-only banner with **Exit preview**. You stay signed in as yourself, nothing is saved, and anything still pending is saved before the preview opens. It is an approximation: rules for specific people or visibility groups, capabilities given to individual users, people with several roles, and plugins that check the user directly can differ. Previews run on the Dashboard only, for roles that can reach the admin. Network super admins on multisite cannot preview. +* **New: preview the menu as a role.** Press **Preview as** (the screen button in the editing toolbar), choose a role and press **Preview**. The Dashboard then shows that role's menu with your rules applied, under a read-only banner with **Exit preview**. You stay signed in as yourself, nothing is saved, and anything still pending is saved before the preview opens. It is an approximation: rules for specific people or visibility groups, capabilities given to individual users, people with several roles, and plugins that check the user directly can differ. Previews run on the Dashboard only, for roles that can reach the admin. Network super admins on multisite cannot preview. * **New: "All other roles" in every role list.** One checkbox at the top ticks or clears every role except your own. It shows a mixed state when only some are ticked. If ticking it would go past the 50-role limit for a list, nothing changes and Maestro tells you why, rather than saving part of the list. * **New: visibility groups.** A visibility group is a named group of people, such as "Reduced view" for a client's staff. Open **Visibility groups** (the people button in the editing toolbar), give the group a name and add people to it. Then tick it in any item's Visibility popover under "Hide this item for visibility groups:" or, on an item with children, "Hide its sub-items for visibility groups:". If you add or remove someone later, every item the group hides follows right away. Deleting a group tells you how many items and people it affects first. The dialog and the help tour explain what a saved group does. * A visibility group is not a role. WordPress never sees it, and it grants or removes nothing. Like every hide in Maestro it is cosmetic, so a hidden page still opens by URL for anyone allowed to see it. Up to 20 groups, with up to 50 people each. @@ -218,12 +218,16 @@ If Maestro saves you time or brings you or your clients the joy of a tidy admin * **Fixed: people who can edit the menu but not list users were sent the user IDs named in per-user hides.** This only matters when a site gives Maestro editing to a role without the `list_users` capability. Those editors now get the menu without that data, and saving keeps the hides they cannot see. * **Improved: phones and tablets.** Tapping a menu item to select it no longer opens the on-screen keyboard. While you rename an item, the editing toolbar rises above the keyboard, and a typed name is kept when you tap another item or turn the device. Dialogs on a phone get a header with a close button. Tablets and other touch screens wider than a phone get the same touch handling. * **Improved: the help tour gives every editing control its own tip,** and on a narrow screen it skips controls that are not shown. +* **Improved: dialogs look and behave alike.** Every editor dialog and the help tour share the same button shapes. A focus outline appears when you use the keyboard, not after a tap, and labels name controls the way the toolbar does. +* **Fixed: deleting an empty visibility group said its people would see items again.** The confirmation now gives the real counts, such as "It hides 0 items from 0 people", and no longer promises that items reappear when another rule still hides them. +* **Fixed: on a phone, choosing another toolbar tool left the first dialog open.** The new tool's dialog now replaces it, and only the open tool is highlighted. +* **Changed: Preview as has its own icon,** a screen with an eye, so it no longer looks like the Visibility button. * **Improved: dialogs that save as you go** (Visibility groups, Menu settings and the help tour) have a close button in their header, and save messages float above the editing toolbar instead of covering it. Toolbar hover colours follow your admin colour scheme. * **Improved: a save error that stays on screen can be dismissed** with the × beside it. That only hides the message: the save still counts as failed, Preview still waits for a good save, and the message comes back with the next save attempt. * **Improved: the Visibility popover and the Visibility groups dialog scroll inside themselves** on a short screen instead of running off it. * **Fixed: on a phone, the help tour jumped from its second tip to its last.** Tapping Next closed WordPress's phone menu, which took the editing toolbar with it, so the tour had nothing left to point at. Every tip now shows, and the count leaves out only the width handle and Menu settings, which phones do not have. * **Fixed: on a phone, a tap inside the icon picker or the Visibility popover closed the editor.** The same cause as the tour. On a phone on its side, the icon picker also opened below the bottom of the screen; it now fits above the toolbar and scrolls. -* **Improved: the editing toolbar is smaller on phones.** Upright, it takes two rows instead of three or four: the name field with the move buttons, then every other control. On its side it takes one. The add and remove separator buttons and, on a phone held upright, Reset All show only their icons there, and keep their names for screen readers and as tooltips. +* **Improved: the editing toolbar is smaller on phones.** Upright, it takes at most two rows, even with an item selected, and on its side it takes one. A row that does not fit scrolls sideways instead of wrapping, so every button keeps a full-size touch target. The help tour button is left out on phones. The add and remove separator buttons and, on a phone held upright, Reset All show only their icons there, and keep their names for screen readers and as tooltips. * **Changed: Menu settings (the gear) shows only on screens 961px and wider.** Its one setting, the menu width, applies only there, so on phones and small tablets it opened a control that changed nothing you could see. A width saved on a wider screen still applies there. * **Fixed: on a phone on its side, edit mode let the page scroll sideways** under the open menu, so phone browsers zoomed the whole screen out. diff --git a/tests/e2e/desktop-refinements.spec.ts b/tests/e2e/desktop-refinements.spec.ts index 3f4cea1..7a8d2a3 100644 --- a/tests/e2e/desktop-refinements.spec.ts +++ b/tests/e2e/desktop-refinements.spec.ts @@ -82,7 +82,14 @@ test.describe( 'touch hover', () => { test( 'touch input does not activate desktop accent hover', async ( { page } ) => { await page.locator( '#menu-posts > a.menu-top' ).tap(); const button = page.locator( '.maestro-vis-btn' ); + // An open tool keeps its solid background on purpose, so close the + // popover with a tap outside it (Escape would return keyboard focus, + // which shows the accent too) before checking that the tap left no + // hover accent behind. await button.tap(); + await expect( button ).toHaveAttribute( 'aria-expanded', 'true' ); + await page.touchscreen.tap( 900, 600 ); + await expect( button ).toHaveAttribute( 'aria-expanded', 'false' ); expect( await page.evaluate( () => matchMedia( '(hover: hover)' ).matches ) ).toBe( false ); const background = await button.evaluate( node => getComputedStyle( node ).backgroundColor ); // Core may retain a subtle translucent hover; the desktop solid accent diff --git a/tests/e2e/specs/menu-width.spec.ts b/tests/e2e/specs/menu-width.spec.ts index 9dde07c..f50417b 100644 --- a/tests/e2e/specs/menu-width.spec.ts +++ b/tests/e2e/specs/menu-width.spec.ts @@ -256,13 +256,18 @@ test.describe( 'Phase 28-03 — menu width controls', () => { }; const panel = visible( '.maestro-panel button, .maestro-panel input' ); const all = visible( '.maestro-toolbar button, .maestro-toolbar input' ); + // Each tool group scrolls sideways inside itself on a phone, so a + // control may sit past the edge of the screen. The groups, which + // clip it, must fit, and the page itself must not scroll. + const zones = visible( '.maestro-toolbar .maestro-panel, .maestro-toolbar .maestro-toolbar-right' ); return { panelControls: panel.length, panelRows: rowsOf( panel ), rows: rowsOf( all ), - left: Math.min( ...all.map( ( r ) => r.left ) ), - right: Math.max( ...all.map( ( r ) => r.right ) ), + left: Math.min( ...zones.map( ( r ) => r.left ) ), + right: Math.max( ...zones.map( ( r ) => r.right ) ), viewport: document.documentElement.clientWidth, + pageWidth: document.documentElement.scrollWidth, }; } ); @@ -275,6 +280,7 @@ test.describe( 'Phase 28-03 — menu width controls', () => { expect( layout.rows ).toBeLessThanOrEqual( maxRows ); expect( layout.left ).toBeGreaterThanOrEqual( 0 ); expect( layout.right ).toBeLessThanOrEqual( layout.viewport ); + expect( layout.pageWidth ).toBeLessThanOrEqual( layout.viewport ); } ); }