Aman-Mittal opened a new pull request, #443:
URL: https://github.com/apache/fineract-backoffice-ui/pull/443

   The responsive track from #436.
   
   ## Why
   
   Five media queries across 289 feature components, one of them
   `prefers-reduced-motion`. On a phone the sidebar was a fixed 240px column 
taking two thirds of the
   width, tables scrolled sideways with everything but the first column out of 
sight, and `100vh` put
   the bottom of every page under the browser chrome.
   
   One shell, two shapes. Above 768px nothing changes. At or below, the sidebar 
becomes a modal
   drawer, tables become cards, and the header sheds what does not fit.
   
   | Sign in | Dashboard | Drawer |
   | --- | --- | --- |
   | <img 
src="https://raw.githubusercontent.com/Aman-Mittal/fineract-backoffice-ui/assets/pr-437/mobile/01-login.png";
 width="240"> | <img 
src="https://raw.githubusercontent.com/Aman-Mittal/fineract-backoffice-ui/assets/pr-437/mobile/02-dashboard.png";
 width="240"> | <img 
src="https://raw.githubusercontent.com/Aman-Mittal/fineract-backoffice-ui/assets/pr-437/mobile/03-drawer.png";
 width="240"> |
   
   The client list, which was a sideways scroll and is now one card per row — 
light and dark:
   
   | | |
   | --- | --- |
   | <img 
src="https://raw.githubusercontent.com/Aman-Mittal/fineract-backoffice-ui/assets/pr-437/mobile/04-table-cards.png";
 width="240"> | <img 
src="https://raw.githubusercontent.com/Aman-Mittal/fineract-backoffice-ui/assets/pr-437/mobile/05-table-cards-dark.png";
 width="240"> |
   
   *(Captured from inside the passing Pixel 7 spec against the mocked backend, 
so they are
   reproducible and cannot drift from what the tests assert. Hosted on a fork 
branch so no binaries
   enter the Apache repository.)*
   
   ## One breakpoint, and why it is in TypeScript
   
   `MOBILE_BREAKPOINT_PX` in the new `ViewportService` is the only breakpoint 
the shell has. It exists
   in code rather than only in CSS for a specific reason: CSS can move the 
sidebar off-canvas, but it
   cannot tell the component that the sidebar is now a **dialog** — that it 
takes `role="dialog"`,
   takes focus, closes on Escape, and must be `inert` while hidden so it is not 
a long run of
   invisible tab stops. Once that is in code it has to be the same number as 
the stylesheet's, which
   is what `scripts/check-responsive.mjs` enforces.
   
   ## What changes below 768px
   
   **The drawer.** `SidebarService` keeps `isCollapsed` and `isDrawerOpen` as 
separate state
   deliberately — collapsing on a desktop should not leave a phone holding an 
overlay nobody opened,
   and rotating back should restore the collapsed column rather than an open 
panel. It closes on the
   backdrop, on Escape, on its own control, and on navigating. That last one 
matters: otherwise the
   page the user asked for renders behind the menu they asked with.
   
   **Tables become cards.** Driven by a `data-label` attribute and CSS, not a 
second template, so a
   column added to `columns()` appears in both layouts and cannot be added to 
one only.
   `DataTableComponent` backs **107 of the 289** feature components, which is 
why this single change
   carries most of the benefit.
   
   **`--content-padding`**, which #437 published on the branding allow-list 
without ever defining — a
   deployment could set it and nothing happened. It exists now, steps down on a 
narrow viewport, and
   is still settable.
   
   **The header** drops business date, render time, the guide, the username and 
the language selector,
   all reachable elsewhere. Navigation, search and sign-out stay, each at 44px.
   
   ## Three bugs found doing this, none visible in a diff
   
   - **The header's narrow rules were declared before its base rules.** Media 
queries add no
     specificity, so every one lost on source order and *nothing was actually 
hidden* — the guide
     button was still rendering at 87×34. Found by measuring the rendered 
buttons, not by reading.
   - **Focus never entered the drawer.** The effect ran before Angular removed 
`inert`, and focusing
     an inert subtree is silently a no-op. Deferred to `afterNextRender`.
   - **`login.component.ts` had `min-height: 100vh`**, pre-dating this work. 
The new check found it.
   
   ## One worth a reviewer's eye
   
   The card rules are in `styles/_common.scss`, not in `DataTableComponent`. 
Angular scopes component
   styles with an `_ngcontent` attribute, and the `<tbody>` cdk-table renders 
into is created by the
   HTML parser rather than the template — so a scoped `tbody` selector never 
matches it. Left as
   `table-row-group` it shrank to its content: the cards measured **227px 
inside a 356px table**. The
   screenshot looked plausible; only measuring the layout chain showed it. 
`_common.scss` already owns
   the base `table[cdk-table]` styling for the same encapsulation reason.
   
   ## What enforces it
   
   **`npm run check:responsive`** — new, wired into `ci.yml`. Three rules with 
one correct answer
   each: a media query that is not the shell breakpoint (outside a documented 
allow-list), `height:
   100vh` anywhere, and an unbounded fixed width above 320px. A fixed width 
paired with a `max-width`
   in the same rule is fine and is not flagged — the first version of this 
check reported
   `guidance-tour` as a false positive, and a check that cries wolf gets muted 
rather than fixed. I
   verified it fails on each violation class rather than trusting a green run.
   
   **`E2E (mobile viewport)`** — new Pixel 7 job in `e2e.yml`, 11 cases 
covering what a regex cannot
   see: that the drawer is genuinely modal, that focus moves in, that `inert` 
applies while closed,
   that a tap dismisses it, that the page does not scroll sideways, that the 
shell is exactly as tall
   as the viewport, and that every header control clears 44px. One case asserts 
the *project's own
   viewport* is under the breakpoint, so widening the device cannot silently 
turn the suite into a
   desktop test that still passes.
   
   Branch protection should require `E2E (mobile viewport)`; the note at the 
top of `e2e.yml` has been
   updated to say so.
   
   ## Verification
   
   | | |
   | - | - |
   | Karma | 826 passed |
   | Vitest | 546 passed |
   | Playwright `mobile` | 11 passed (new) |
   | Playwright `mocked` | 338 passed, 1 failed |
   | lint / format / typecheck (app + e2e) | clean |
   | `check:responsive`, `check:nav-ids`, `check:branding-path`, 
`check:reference-downstream` | pass |
   | `i18n:check`, `check:icons`, `check:a11y-names` | pass |
   
   The one `mocked` failure is `accessibility.spec.ts › client list`, which 
**passes in 1.2m when run
   alone** and also fails on `main` without this branch — a 30s-timeout flake 
under full-suite load,
   not a regression. CI shards that suite, so it has more headroom there.
   
   ## Not included
   
   `primaryColumns` on `DataTableComponent` — collapsing a card to its most 
important fields with the
   rest behind a disclosure. Worth having, not needed for the layout to be 
usable, and it would add an
   input to a component 107 screens depend on. Better as its own change.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to