Skip to content

WEBUI-2040: Ability to adjust the width of the Browse pane, Search pane, and Information pane [LTS-2023]#3186

Open
naveen-konda wants to merge 12 commits into
maintenance-3.1.xfrom
WEBUI-2040-ability-to-adjust-the-width-of-the-sidenav-pane-and-information-pane
Open

WEBUI-2040: Ability to adjust the width of the Browse pane, Search pane, and Information pane [LTS-2023]#3186
naveen-konda wants to merge 12 commits into
maintenance-3.1.xfrom
WEBUI-2040-ability-to-adjust-the-width-of-the-sidenav-pane-and-information-pane

Conversation

@naveen-konda
Copy link
Copy Markdown
Contributor

@naveen-konda naveen-konda commented May 25, 2026

ELEMENTS-1844 / WEBUI-2040 — Feature Summary

Resizable Browse / Search drawer and Information pane in Nuxeo Web UI.

Shipping ticket: WEBUI-2040Ability to adjust the width of the Browse pane, Search pane, and Information pane (PR #3186).

Note: Browse and Search are not separate layouts — both use the same left drawer in nuxeo-app (different drawer tabs). Only the Information pane on document pages is a second resizable panel.

Dual-repo shipping: UI work spans nuxeo-web-ui (WEBUI-2040) and nuxeo-elements (ELEMENTS-1844 — shared nuxeo-resize-handle widget, i18n keys, tooltip clone styling). Both must land (elements RC published before or with web-ui) for CI to resolve imports.

At a glance (PM / QA)

Question Answer
What can users resize? Left: Browse/Search drawer (all routes). Right: Information pane (document pages only).
How? 6 px edge — drag, keyboard, double-click / Enter to reset.
Remembered? Yes — per browser (localStorage).
Breaks mobile? No — handles hidden ≤720 px (drawer) / ≤1024 px stacked layout (info pane).
Squeezes the document? No — main column keeps ≥ half of doc-page width (240–640 px band).
Two panes fighting? Info pane can ask app to shrink drawer (nuxeo-shrink-drawer).
Ship order nuxeo-elements RC first (or same release train), then web-ui.
RTL locales Handles stay on the inner edge toward main content; arrow keys mirror (§ 7a).

1. What this feature does

Users can now drag the edge of two side panels to make them wider or narrower:

  • Left panel — the Browse / Search drawer that opens when you click an icon on the side-nav.
  • Right panel — the Information pane on document detail pages.

The chosen width is remembered across page refreshes (per browser).

That's it. No new buttons, no new menus — just a draggable edge.


2. How it looks

Wide desktop (document page) — the info pane lives inside the main column, not beside the whole app:

┌ nuxeo-app ─────────────────────────────────────────────────────────┐
│ sidebar │ Browse/Search drawer │  #mainContent (any route)         │
│  52 px  │  ⇨ drag here         │  ┌ nuxeo-document-page ────────┐ │
│         │                      │  │ document view │ Info ⇦ drag │ │
│         │                      │  │    (.main)    │  (.side)    │ │
│         │                      │  └──────────────────────────────┘ │
└────────────────────────────────────────────────────────────────────┘

On search / tasks / admin routes, only the drawer handle applies (no info pane).

Each handle is a **nuxeo-resize-handle** strip (6 px). It is invisible until hover/focus (--nuxeo-resize-handle-color per theme). Double-click or Enter / Space (when focused) resets that pane to its default width.


3. What was added (high level)

Area What changed
Shared widget **nuxeo-resize-handle** in nuxeo-elements — 6 px strip, pointer/keyboard/RTL, tooltip, ARIA; hosts listen for resize-* events.
Left drawer Resize logic in **NuxeoAppDrawerResizeBehavior** (elements/behaviors/nuxeo-app-drawer-resize-behavior.js); shell wiring in nuxeo-app.js.
Right info pane Clamp math, persistence, push-back in elements/document/nuxeo-document-page.js; same widget in the template.
Memory Widths stored in localStorage (nuxeo.drawerWidth, nuxeo.documentPage.sidePaneWidth).
Coordination If the info pane needs more width at its cap, it fires nuxeo-shrink-drawer so the drawer shrinks and the main column stays safe.
Layout notify App-level **_notifyLayoutChanged()**notifyResize() + guarded window.resize for all main-content routes; drawer drag uses iron-resize only (no global resize per mousemove). Info pane fires **nuxeo-layout-updated** on settle so zoom reflows even when the drawer did not move.
Safety The document content is always guaranteed a usable minimum width — neither side panel can squeeze the main view to nothing.
Themes --nuxeo-resize-handle-color in default, light, dark, and kawaii themes (hover/focus on the widget). Tooltip label styles live in nuxeo-elements (nuxeo-tooltip clone), not themes/base.js.
Translations English keys **app.drawer.resize**, **documentPage.resize.side** in nuxeo-elements ui/i18n/messages.json (Crowdin sync as usual).
Tests test/nuxeo-app-drawer-resize-behavior.test.js, test/nuxeo-document-page.test.js, test/nuxeo-app.test.js (RTL integration); nuxeo-elements ui/test/nuxeo-resize-handle-extras.test.js, nuxeo-tooltip.test.js.

No existing screens, layouts, or workflows were changed when the user has not resized anything.


3a. Charts — layout, resize, and who gets updated

Where each handle lives (accurate nesting)

flowchart TB
  subgraph app ["nuxeo-app"]
    SB["Sidebar\n52 px"]
    DR["Drawer\n+ nuxeo-resize-handle\nedge=end ⇨"]
    subgraph main ["#mainContent — all routes"]
      ROUTE["search · tasks · admin · …"]
      subgraph doconly ["document route only"]
        DV[".main — document view"]
        INFO[".side — info pane\n+ nuxeo-resize-handle\nedge=start ⇦"]
      end
    end
  end
  SB --- DR --- main
  DV --- INFO
Loading
Handle Visible when Affects
Drawer Drawer open and viewport > 720 px Width of every main-content page
Info pane Info pane open and wide layout ≥ 1025 px Document page only

Three ideas to keep straight

flowchart TB
  subgraph perPane ["Per pane"]
    PREF["User preference\nlocalStorage"]
    PAINT["What you see now\ninline width / CSS var"]
    LEGAL["Legal range right now\nmin … max for this screen"]
  end
  PREF --> LEGAL
  LEGAL --> PAINT
Loading

On zoom or window resize, preference in storage stays; min/max change; code re-clamps painted width back toward preference (same pattern for drawer and info pane).

When widths are saved

User action Saved to localStorage Layout reflow to rest of app
Drawer drag while moving No iron-resize only (tables reflow, no global window.resize storm)
Drawer drag release Yes Full settle (notifyResize + window.resize)
Drawer arrow / Home / End Yes (each step) Full settle
Info pane drag while moving No Bubbling resize on document-page only
Info pane drag release Yes nuxeo-layout-updated → app full settle
Info pane arrow / Home / End Yes (each step) nuxeo-layout-updated → app full settle
Browser zoom / window resize No (preference unchanged) Re-clamp both panes + full settle

How main content learns the width changed

flowchart LR
  subgraph during ["While dragging"]
    D1["Drawer drag"]
    D2["Info drag"]
    D1 --> IR1["iron-resize only"]
    D2 --> R2["resize event\non document-page"]
  end
  subgraph settle ["On settle / zoom"]
    S1["Drawer: release,\nkeys, reclamp"]
    S2["Info: release,\nkeys, reclamp"]
    S2 --> E["nuxeo-layout-updated"]
    S1 --> N["_notifyLayoutChanged"]
    E --> N
    N --> IR2["iron-resize"]
    N --> WR["window.resize\n(guarded)"]
  end
  IR1 --> W["Tables · viewers\nin main column"]
  IR2 --> W
  WR --> W
Loading
Channel Who cares When
notifyResize()iron-resize Data tables, picture viewer, etc. Drawer drag move and every settle
window.resize App narrow flag, info-pane reclamp, legacy code Settle only (not each drawer drag frame)
nuxeo-layout-updated nuxeo-app listener Info pane settle / zoom reclamp

Not page-by-page: the shell does not special-case routes; widgets must listen via IronResizableBehavior or window.resize.

Contract: implementation-strategy §3.4a.

When both panes compete for space (push-back)

sequenceDiagram
  participant U as User
  participant I as Info pane
  participant A as nuxeo-app
  participant D as Drawer

  U->>I: Drag info pane past its max
  I->>A: nuxeo-shrink-drawer { amount }
  A->>D: Shrink drawer (min floor applies)
  Note over I,D: Main document column stays ≥ reserved width
Loading

If the drawer is already at its minimum, the info pane stops growing — no error, no layout break.

Zoom in / out (drawer vs info pane)

sequenceDiagram
  participant User
  participant Browser
  participant App as nuxeo-app
  participant Doc as document-page
  participant LS as localStorage

  User->>Browser: Zoom in or out
  Browser->>App: window resize
  App->>App: Re-clamp drawer from LS preference
  App->>App: notifyResize + window.resize
  Browser->>Doc: window resize
  Doc->>Doc: Next frame: re-clamp info pane from LS preference
  Doc->>App: nuxeo-layout-updated
  App->>App: notifyResize + window.resize
  Note over LS: Saved widths unchanged unless user releases drag
Loading

Responsive breakpoints (when handles appear)

flowchart LR
  W["Viewport width"]
  W --> A["≥ 1025 px\nBoth handles\n(side-by-side doc)"]
  W --> B["721–1024 px\nDrawer handle only\ninfo stacks below doc"]
  W --> C["≤ 720 px\nNo handles\nmobile drawer overlay"]
Loading
Effective width Drawer handle Info handle Layout
≥ 1025 px Yes (if drawer open) Yes (if info open) Doc + info side by side
721–1024 px Yes (if drawer open) No Info below document
≤ 720 px No No Drawer overlay; stacked doc

Browser zoom changes effective CSS pixels the same way as resizing the window.

Release train (dual repo)

flowchart LR
  E["nuxeo-elements PR\nnuxeo-resize-handle\n+ i18n + tooltip"]
  RC["Published @nuxeo/nuxeo-ui-elements RC"]
  W["nuxeo-web-ui PR\nbehavior + hosts"]
  CI["CI: npm ci + latest RC"]
  E --> RC --> W --> CI
Loading

What ships in the PRs

nuxeo-web-ui (WEBUI-2040):

  • elements/nuxeo-app.js — shell, layout notify, shrink-drawer listener, nuxeo-layout-updated consumer
  • elements/behaviors/nuxeo-app-drawer-resize-behavior.js — drawer width math, persistence, resize handlers
  • elements/document/nuxeo-document-page.js — info-pane math, persistence, push-back, nuxeo-layout-updated producer
  • themes/*/theme.html--nuxeo-resize-handle-color
  • test/nuxeo-app-drawer-resize-behavior.test.js, test/nuxeo-document-page.test.js, test/nuxeo-app.test.js
  • docs/ELEMENTS-1844-*.md

nuxeo-elements (coordinated release — nuxeo-resize-handle, tooltip clone fix, i18n keys):

  • ui/widgets/nuxeo-resize-handle.js, ui/widgets/nuxeo-tooltip.js
  • ui/nuxeo-ui-elements.js, ui/i18n/messages.json
  • ui/test/nuxeo-resize-handle-extras.test.js, ui/test/nuxeo-tooltip.test.js

CI installs the latest @nuxeo/nuxeo-ui-elements RC from the registry — the elements PR must publish an RC that includes nuxeo-resize-handle before web-ui CI passes on the widget refactor.


4. How the resize works in practice

What happens while you drag

  1. The user focuses or grabs a **nuxeo-resize-handle** edge (mouse, touch, or Tab).
  2. While dragging, width updates live; the drawer skips global window.resize each frame (performance).
  3. On pointer release, width is written to localStorage and the app runs a full layout settle.
  4. Each keyboard arrow / Home / End step saves immediately and settles layout.
  5. On the next visit, panes restore from localStorage (clamped to the current screen).
  6. On zoom or window resize, stored preference is kept; painted width is re-clamped to the new legal range (a temporary shrink while zoomed in does not overwrite what the user saved).

What stops the user from making things unusable

Each pane has a minimum width (so labels and icons never get cut off) and a maximum width (so the rest of the page is never squashed).

  • Browse / Search drawer minimum: 350 px — natural open width (298 px content + sidebar). Below this, navigation labels start to wrap.
  • Information pane minimum: 280 px — safe floor below which Info / Properties / Collections cards start to clip.
  • The maximum on either side is whatever leaves enough room for the main document content. (See § 5.)

If the user tries to drag past these limits, the pane simply stops moving — there is no jump, no error, no jarring snap.

Resetting back to the default

  • Double-click the edge, or
  • Focus the edge with the keyboard and press Enter or Space.

5. How the main document content stays readable

This is the most important behaviour to understand.

The document view (the middle column) is the focus of the user's work. The two side panels exist to support it. So the implementation reserves space for the main column before the side panels are allowed to grow.

The reservation rule is: the main column is guaranteed at least half of the document-page container's width, capped at a comfortable 640 px on very large screens and floored at 240 px on very small ones. That gives readers enough room for data tables and document content even when both panes are maxed out.

In plain numbers — both panes dragged to their maximum at that viewport (drawer max is dynamic, grows when you zoom out):

Viewport Drawer max Info-pane max Main column ≥
1920 × 1080 960 px 610 px 610 px
1600 × 900 800 px 450 px 450 px
1440 × 900 700 px 370 px 370 px
1366 × 768 683 px 341 px 342 px
1280 × 720 640 px 320 px 320 px

If the user drags the info pane bigger and the drawer is already wide, the drawer automatically shrinks to make room — the main column never drops below its reserved minimum.

The reservation is proportional, so it adapts to smaller screens and to browser zoom. Below the 1024 px breakpoint the document page switches to vertical stacking (main on top, info below) and resize edges are hidden — matching the stock responsive behaviour.


6. Different screen sizes and browser zoom

The feature follows the same responsive breakpoints the rest of Nuxeo Web UI already uses:

Viewport Behaviour
Wide (≥ 1025 px) Resize edges are visible and active on both panes.
Tablet (721 – 1024 px) Document view switches to vertical stacking (main on top, info below). Resize edges on the info pane are hidden. The drawer is still side-by-side and resizable.
Mobile (≤ 720 px) Drawer turns into a mobile overlay opened from the hamburger menu. Info pane stacks below the main view. No resize edges.

Browser zoom is handled the same way as window resizing:

  • At 100 % zoom, the user gets the full feature.
  • At 175 %, the document view stays visible and the panels can still be resized.
  • At 250 % or more, the layout drops into mobile overlay mode automatically (matching the existing app behaviour).
  • Coming back down from a high zoom level, the previously-open drawer is restored to its saved width automatically — including the case where the user opened the drawer while in narrow / overlay mode and then zoomed back out.
  • After any zoom transition, layout-dependent components re-flow — picture viewers, adaptive data tables, etc. — via the app shell: drawerPanel.notifyResize() (iron-resize) and a guarded window.resize. This applies to whatever page is open in main content, not only the document view.
  • Info pane width is re-applied from localStorage after zoom (so it matches the drawer: refresh is not required to restore a widened info pane).

7. Accessibility & keyboard support

The draggable edges are full keyboard widgets, not just mouse targets. They show up in the keyboard tab order and announce themselves to screen readers.

Input What it does
Tab Focuses the resize edge.
/ arrow Resizes by 16 px.
Shift + arrow Resizes by 64 px (faster).
Home Jumps to minimum width.
End Jumps to maximum width.
Enter or Space Resets to default width (documented in the aria-label).
Double-click Resets to default width.

The mouse cursor changes to a horizontal-resize arrow when hovering. Keyboard **:focus-visible** uses an outline in --nuxeo-resize-handle-color (same token as the hover fill).

Labels:

  • Both handles use **label-key** on nuxeo-resize-handle → translated aria-label plus **nuxeo-tooltip** on hover (styles scoped on the tooltip clone, not global theme CSS).
  • Keys: app.drawer.resize (drawer), documentPage.resize.side (info pane).

Example SR text: "Resize the side navigation panel. Use Left/Right arrow keys to adjust width, Home or End for minimum or maximum, Enter or Space to reset."

RTL (Arabic, Hebrew): see § 7a — mirrored arrows, handle placement, and QA.


7a. LTR vs RTL (Arabic, Hebrew)

The UI follows the app / page **dir** attribute. Resize behaviour is mirrored so users always widen a pane by dragging away from the main content and narrow it by dragging toward the main content. Keyboard Left/Right are remapped the same way (labels still say “Left/Right arrow keys” in English).

Handle placement (inner edge toward main)

The widget uses edge + explicit dir (not CSS :host-context through app-drawer shadow DOM).

Pane edge dir from host Handle sits on (visual)
Browse / Search drawer end nuxeo-app _resizeHandleDir(_isRTL) LTR: right side of drawer · RTL: left side of drawer
Information pane start nuxeo-document-page _resizeHandleDir (page dir) LTR: left side of info pane · RTL: right side of info pane
LTR — document page (top view, simplified)

  [sidebar][  drawer  |⇨|  main + doc view  |⇦|  info ]

RTL — same structure mirrored

  [sidebar][|⇦  drawer  ][  main + doc view  |⇨|  info ]
            ↑ handle on drawer’s inner edge toward main

Tooltip hover position flips with direction (_drawerResizeTooltipPosition / _sideResizeTooltipPosition).

Which arrow widens the pane?

Logic lives in **nuxeo-resize-handle** (resizeDeltaForKey). “Widen” means increase pane width in pixels.

Pane Edge Direction Arrow that widens Arrow that narrows
Drawer end LTR Right Left
Drawer end RTL Left Right
Info pane start LTR Left Right
Info pane start RTL Right Left

Home / End still mean minimum / maximum width in both directions. Shift + arrow keeps the same widen/narrow mapping with a 64 px step.

Pointer drag (mouse / touch)

Drag uses resizeDeltaFromPointer: the sign of movement is flipped in RTL so the same physical gesture (pull the inner edge away from the document) widens the pane in both LTR and RTL. QA should verify drag direction feels natural in an RTL locale, not only arrow keys.

flowchart LR
  subgraph widget ["nuxeo-resize-handle"]
    D["dir=ltr | rtl"]
    E["edge=start | end"]
    K["resizeDeltaForKey\nresizeDeltaFromPointer"]
  end
  subgraph hosts ["Hosts apply delta"]
    APP["nuxeo-app\n_drawerResizeStep…"]
    DOC["nuxeo-document-page\n_onSideResizeStep…"]
  end
  D --> K
  E --> K
  K --> APP
  K --> DOC
Loading

What QA should check in RTL

Check Pass criteria
Handle visible Both handles on the inner edge (toward main), not missing inside app-drawer
Drag widen / narrow Dragging away from the document widens; toward document narrows
Arrow keys Widening/narrowing matches the table above
Push-back Growing info pane past max still fires nuxeo-shrink-drawer; drawer shrinks
Persistence localStorage keys unchanged; width restores after refresh

Automated coverage: test/nuxeo-app.test.js (drawer handle), test/nuxeo-document-page.test.js and test/nuxeo-app-drawer-resize-behavior.test.js (RTL suites), nuxeo-elements ui/test/nuxeo-resize-handle.test.js (delta helpers).


8. Persistence

The chosen widths are saved in the browser's localStorage:

Pane Key
Browse / Search drawer nuxeo.drawerWidth
Information pane nuxeo.documentPage.sidePaneWidth
  • Per-browser, per-machine (not synced across devices — same as theme / language today).
  • Restored on every page load (drawer on ready; info pane on attached via animationFrame).
  • On zoom or window resize, reclamp reads saved preference again (not only the last in-memory value) — same idea as the drawer.
  • If the saved value is too large for the current screen, it is clamped automatically.
  • Reset clears the key and restores default flex / natural widths.
  • If localStorage is unavailable (private mode, quota, etc.), the panes use defaults and errors are ignored.

9. Things product / QA should be aware of

  1. No new server calls. The feature is purely client-side. No new API, no new permissions.
  2. No effect on existing layouts when the user has not resized anything — defaults are unchanged.
  3. Already-open documents survive zooming. If you zoom in past the mobile breakpoint, the drawer collapses; zooming back out automatically restores it at the user's saved width when the inline width still looks "open".
  4. Main column is guaranteed half the document-page container (cap 640 px, floor 240 px). On a 1920 × 1080 monitor that's at least 610 px even when both panes are dragged to max.
  5. Layout reflow is globalnuxeo-app runs notifyResize on every notify path; synthetic window.resize only on settle (not each drawer drag frame). Info pane uses nuxeo-layout-updated to trigger the same settle path. See § 3a.
  6. Future pages — if a screen looks wrong after resize/zoom, check whether its components use those listeners; the shell does not special-case each route.
  7. Themes: resize-edge hover/focus uses **--nuxeo-resize-handle-color**, not the primary brand color.
  8. Tablet gap (721–1024 px): drawer is resizable; info-pane handle is hidden by design (stacked layout).

Minimum QA pass (before merge)

# Steps Expected
1 100 % zoom — drag drawer and info pane to max, refresh Widths restored; main column still usable
2 Max info + max drawer Drawer shrinks or panes hit limits; no crushed main view
3 Double-click each handle Default width; localStorage key cleared
4 Keyboard: Tab to handle, arrows, Home, End, Enter Same as drag limits; reset on Enter
5 Zoom 100 % → 150 % → 100 % with drawer open Drawer reclamps; tables/viewers reflow without hover
6 Zoom past 250 % then back Overlay drawer; restore saved width on zoom out
7 RTL locale or dir=rtl Per § 7a: handles on inner edge; arrows + drag widen/narrow correctly
8 Route: open Search (not document) Drawer handle works; no info handle

10. Files to look at

File Repo Purpose
ui/widgets/nuxeo-resize-handle.js elements Shared resize strip: input, RTL, events, ARIA helpers.
ui/widgets/nuxeo-tooltip.js elements Tooltip clone styling for resize labels (no leak into themes/base.js).
elements/behaviors/nuxeo-app-drawer-resize-behavior.js web-ui Drawer math, persistence, resize handlers, push-back consumer.
elements/nuxeo-app.js web-ui Shell, **_notifyLayoutChanged / _runLayoutNotify**, nuxeo-layout-updated listener.
elements/document/nuxeo-document-page.js web-ui Info-pane math, push-back producer, **nuxeo-layout-updated** on settle/zoom.
ui/i18n/messages.json elements app.drawer.resize, documentPage.resize.side.
themes/*/theme.html web-ui --nuxeo-resize-handle-color.
test/nuxeo-app-drawer-resize-behavior.test.js web-ui Drawer unit tests (math, layout notify, RTL, shrink-drawer).
test/nuxeo-document-page.test.js web-ui Info-pane unit tests (incl. RTL, nuxeo-layout-updated).
test/nuxeo-app.test.js web-ui App shell RTL integration on #drawerResizeHandle.

TL;DR

Users resize the Browse/Search drawer (all routes) and Information pane (document pages) via **nuxeo-resize-handle. Widths persist in **nuxeo.drawerWidth** and **nuxeo.documentPage.sidePaneWidth**. Main content keeps ≥ half of the doc-page width (240–640 px band). RTL: inner-edge handles + mirrored arrows/drag (§ 7a). While dragging: light reflow (iron-resize / local resize). On settle: app runs notifyResize + guarded window.resize; info pane signals **nuxeo-layout-updated. Info pane can **nuxeo-shrink-drawer**. Ship elements RC then web-ui. WEBUI-2040 / ELEMENTS-1844. Diagrams: § 3a · QA: § 9.

Copilot AI review requested due to automatic review settings May 25, 2026 06:02
@naveen-konda naveen-konda requested a review from a team as a code owner May 25, 2026 06:02
@naveen-konda naveen-konda requested review from AnilKumarVanga and Nishant0928 and removed request for a team May 25, 2026 06:02
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Implements user-adjustable pane widths for the left navigation drawer and the document “Information” side pane, including persistence of preferred widths across sessions and basic keyboard/drag interactions.

Changes:

  • Adds a draggable + keyboard-accessible resize handle to the app drawer, with width clamping and localStorage persistence.
  • Adds a draggable + keyboard-accessible resize handle to the document page side pane, with clamping, persistence, and coordination to “push back” the drawer when needed.
  • Introduces a themeable CSS custom property for resize-handle styling and new i18n strings for resize instructions.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
themes/light/theme.html Adds --nuxeo-resize-handle-color for light theme.
themes/kawaii/theme.html Adds --nuxeo-resize-handle-color for kawaii theme.
themes/default/theme.html Adds --nuxeo-resize-handle-color for default theme.
themes/dark/theme.html Adds --nuxeo-resize-handle-color for dark theme (adjusted color for contrast).
i18n/messages.json Adds i18n strings describing keyboard resize interaction for drawer and info pane.
elements/nuxeo-app.js Implements drawer resize handle, width clamping, persistence, and layout notifications.
elements/document/nuxeo-document-page.js Implements info-pane resize handle, width clamping, persistence, and drawer “shrink” coordination.
Comments suppressed due to low confidence (3)

elements/nuxeo-app.js:1546

  • Drawer width persistence/clamping and the new keyboard resizing paths (_computeOpenDrawerWidth, _onDrawerResizeKey, _resetDrawerWidth, _onShrinkDrawerRequest) are not covered by unit tests, despite test/nuxeo-app.test.js existing for this element. Adding a few focused tests would help prevent regressions (e.g., clamping behavior, storage load/save/reset, Arrow/Home/End behavior).
  /** Open drawer width (px): stored preference or natural default, clamped. */
  _computeOpenDrawerWidth() {
    const fallback = DRAWER_NATURAL_CONTENT_PX + this._sidebarPx();
    const stored = this._drawerOpenWidth != null ? this._drawerOpenWidth : this._loadStoredDrawerWidth();
    if (stored == null) {
      return fallback;
    }
    this._drawerOpenWidth = stored;
    return this._clampDrawerWidth(stored);
  },

  _clampDrawerWidth(px) {
    return Math.min(this._maxDrawerWidth(), Math.max(this._minDrawerWidth(), px));
  },

  _loadStoredDrawerWidth() {
    try {
      const raw = window.localStorage && window.localStorage.getItem(DRAWER_STORAGE_KEY);
      const n = raw != null ? Number.parseInt(raw, 10) : NaN;
      return Number.isFinite(n) ? n : null;
    } catch (_e) {
      return null;
    }
  },

  _persistDrawerWidth(px) {
    try {
      if (window.localStorage) {
        window.localStorage.setItem(DRAWER_STORAGE_KEY, String(px));
      }
    } catch (_e) {
      // ignore storage errors (e.g. private mode quota)
    }
  },

  _onDrawerResizeStart(e) {
    if (!this.drawerOpened || this.isNarrow) {
      return;
    }
    e.preventDefault();
    e.stopPropagation();
    const point = e.touches && e.touches[0] ? e.touches[0] : e;
    const startX = point.clientX;
    const startWidth = this._computeOpenDrawerWidth();
    this.setAttribute('drawer-resizing', '');
    const rtl = this._isRTL;

    const onMove = (ev) => {
      const p = ev.touches && ev.touches[0] ? ev.touches[0] : ev;
      const delta = (p.clientX - startX) * (rtl ? -1 : 1);
      const next = this._clampDrawerWidth(startWidth + delta);
      this._drawerOpenWidth = next;
      this.drawerWidth = `${next}px`;
      this._scheduleDrawerDragLayoutNotify();
    };

    const onEnd = () => {
      this._cancelDrawerDragLayoutNotify();
      this.removeAttribute('drawer-resizing');
      window.removeEventListener('mousemove', onMove);
      window.removeEventListener('mouseup', onEnd);
      window.removeEventListener('touchmove', onMove);
      window.removeEventListener('touchend', onEnd);
      if (this._drawerOpenWidth != null) {
        this._persistDrawerWidth(this._drawerOpenWidth);
      }
      this._notifyLayoutChanged();
    };

    window.addEventListener('mousemove', onMove);
    window.addEventListener('mouseup', onEnd);
    window.addEventListener('touchmove', onMove, { passive: false });
    window.addEventListener('touchend', onEnd);
  },

  _onDrawerResizeKey(e) {
    if (!this.drawerOpened || this.isNarrow) {
      return;
    }
    const step = e.shiftKey ? DRAWER_KEY_STEP_SHIFT_PX : DRAWER_KEY_STEP_PX;
    const current = this._computeOpenDrawerWidth();
    const rtl = this._isRTL;
    let next;
    switch (e.key) {
      case 'ArrowLeft':
        next = current + (rtl ? step : -step);
        break;
      case 'ArrowRight':
        next = current + (rtl ? -step : step);
        break;
      case 'Home':
        next = this._minDrawerWidth();
        break;
      case 'End':
        next = this._maxDrawerWidth();
        break;
      case 'Enter':
      case ' ':
        this._resetDrawerWidth();
        e.preventDefault();
        return;
      default:
        return;
    }
    e.preventDefault();
    next = this._clampDrawerWidth(next);
    this._drawerOpenWidth = next;
    this.drawerWidth = `${next}px`;
    this._persistDrawerWidth(next);
    this._notifyLayoutChanged();
  },

elements/document/nuxeo-document-page.js:463

  • sideWidth already has reflectToAttribute: true, but _sideWidthChanged also manually sets/removes the side-width attribute. This is redundant and can lead to confusing state (attribute may be mutated from two different mechanisms). It should be enough to only update the CSS custom property and let Polymer handle attribute reflection.
  _sideWidthChanged(value) {
    if (value == null || Number.isNaN(Number(value))) {
      this.style.removeProperty('--nuxeo-side-pane-width');
      this.removeAttribute('side-width');
    } else {
      this.style.setProperty('--nuxeo-side-pane-width', `${value}px`);
      this.setAttribute('side-width', String(value));
    }

elements/document/nuxeo-document-page.js:419

  • The new side-pane width persistence and resizing logic (sideWidth, localStorage load/persist, clamping, keyboard/drag handlers, and nuxeo-shrink-drawer emission) is not covered by unit tests, even though test/nuxeo-document-page.test.js exists. Adding tests around clamping bounds and storage load/reset would reduce regression risk.
    /** Info pane width (px); null uses default flex layout. */
    sideWidth: {
      type: Number,
      value: null,
      notify: true,
      reflectToAttribute: true,
      observer: '_sideWidthChanged',
    },
  },

  ready() {
    if (!this.hasAttribute('dir')) {
      const direction = document.documentElement.getAttribute('dir');
      this.setAttribute('dir', direction);
    }
    this._pendingStoredSideWidth = this._loadStoredSideWidth();
  },

  attached() {
    // Re-clamp on viewport/drawer changes (numeric only — no DOM flicker).
    this._onWindowResize = () => {
      this._reclampSideWidth();
    };
    window.addEventListener('resize', this._onWindowResize);

    animationFrame.run(() => {
      if (this._pendingStoredSideWidth != null) {
        if (this._isNarrowViewport()) {
          // Keep stored width unclamped; narrow CSS ignores [side-width].
          this.sideWidth = this._pendingStoredSideWidth;
        } else {
          this.sideWidth = this._clampSideWidth(this._pendingStoredSideWidth);
        }
        this._pendingStoredSideWidth = null;
      }
    });
  },

Comment thread elements/nuxeo-app.js Outdated
Comment thread elements/document/nuxeo-document-page.js Outdated
@naveen-konda naveen-konda changed the title WEBUI-2040: Ability to adjust the width of the Browse pane, Search pane, and Information pane WEBUI-2040: Ability to adjust the width of the Browse pane, Search pane, and Information pane [LTS-2023] May 26, 2026
@sonarqubecloud
Copy link
Copy Markdown

Comment thread test/nuxeo-diff.test.js
Comment thread elements/document/nuxeo-document-page.js
Comment thread elements/document/nuxeo-document-page.js
Comment thread elements/nuxeo-app.js
Comment thread elements/nuxeo-app.js
Comment thread elements/nuxeo-app.js
Comment thread test/nuxeo-document-page.test.js
Comment thread test/nuxeo-document-page.test.js
Comment thread test/nuxeo-app.test.js
Comment thread elements/document/nuxeo-document-page.js
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants