diff --git a/README.md b/README.md index dbd13d4596..22285578c8 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ PastureStack is an independent community effort to preserve, audit, and moderniz ## Project status -The current source compatibility target is `1.6.154`. It retains the existing Node 24, Ember, Sass, +The current source compatibility target is `1.6.155`. It retains the existing Node 24, Ember, Sass, dependency, browser-smoke, terminal, console, and test-harness modernization. It adds a provider-neutral OpenID Connect administration and sign-in flow with PKCE S256, staged configuration validation, a real test login before @@ -16,13 +16,21 @@ activation, and local-authentication recovery. Product-owned names, logos, icons, package metadata, and visible text use PastureStack branding. API models and protocol fields remain compatible. +Release `1.6.155` positions global notices below the navigation bar using the +existing navbar height and keeps them inside narrow viewports. This corrects +the mobile layout overlap found during `1.6.154` acceptance without changing +authorization or API behavior. See the +[release note](docs/releases/web-console-1.6.155.md) for focused visual tests; +packaged Server/browser acceptance must be checked against a new image. + Release `1.6.154` shows a localized, persistent permission error when a direct Stack or Service creation URL is denied, then returns to the Stacks list. Service upgrade URLs continue to use update permission and receive an update error only when that permission is absent. The shared message covers all resource types using the route guard. See the -[release note](docs/releases/web-console-1.6.154.md) for source test evidence; -packaged browser and Server acceptance remain pending. +[release note](docs/releases/web-console-1.6.154.md) for source test evidence. +It was packaged in Server `v1.6.488`; its 8080 browser acceptance identified +the notification/navbar overlap addressed by `1.6.155`. Release `1.6.153` makes Stack, Service, and Container write controls check their current project/resource capability when the user acts; delayed project diff --git a/app/styles/app-dark.scss b/app/styles/app-dark.scss index 7c4ad23e48..2ac91661a2 100644 --- a/app/styles/app-dark.scss +++ b/app/styles/app-dark.scss @@ -643,7 +643,7 @@ $k8sHeader: theme.$k8sHeader; // // Component-specific files. //****************************************** -@include meta.load-css("app/styles/components/growl", $with: ("brand-danger": $brand-danger)); +@include meta.load-css("app/styles/components/growl", $with: ("brand-danger": $brand-danger, "navbar-height": $navbar-height)); @include meta.load-css("app/styles/components/resource-actions", $with: ("text-color": $text-color)); @include meta.load-css("app/styles/components/login", $with: ("primary": $primary)); @include meta.load-css("app/styles/components/mfa"); diff --git a/app/styles/app-light.scss b/app/styles/app-light.scss index aa3e9d98a5..e9fba78fed 100644 --- a/app/styles/app-light.scss +++ b/app/styles/app-light.scss @@ -643,7 +643,7 @@ $k8sHeader: theme.$k8sHeader; // // Component-specific files. //****************************************** -@include meta.load-css("app/styles/components/growl", $with: ("brand-danger": $brand-danger)); +@include meta.load-css("app/styles/components/growl", $with: ("brand-danger": $brand-danger, "navbar-height": $navbar-height)); @include meta.load-css("app/styles/components/resource-actions", $with: ("text-color": $text-color)); @include meta.load-css("app/styles/components/login", $with: ("primary": $primary)); @include meta.load-css("app/styles/components/mfa"); diff --git a/app/styles/components/_growl.scss b/app/styles/components/_growl.scss index 0a5f3b0959..5dbfbff5bf 100644 --- a/app/styles/components/_growl.scss +++ b/app/styles/components/_growl.scss @@ -1,6 +1,9 @@ $brand-danger: null !default; +$navbar-height: null !default; .jGrowl.top-right { + // jGrowl's notification margin supplies the gap below the top navigation. + top: $navbar-height; z-index: 10; } @@ -14,6 +17,8 @@ $brand-danger: null !default; .jGrowl-notification, .jGrowl-closer { width: 300px; + // Leave room for jGrowl's 10px margin on each side of a narrow viewport. + max-width: calc(100vw - 20px); opacity: 1; color: white; } diff --git a/docs/baselines/npm-package-lock.sass-replacement.node24-ignore-scripts.json b/docs/baselines/npm-package-lock.sass-replacement.node24-ignore-scripts.json index 80871a793c..b106cb37d7 100644 --- a/docs/baselines/npm-package-lock.sass-replacement.node24-ignore-scripts.json +++ b/docs/baselines/npm-package-lock.sass-replacement.node24-ignore-scripts.json @@ -1,12 +1,12 @@ { "name": "@pasturestack/web-console", - "version": "1.6.154", + "version": "1.6.155", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@pasturestack/web-console", - "version": "1.6.154", + "version": "1.6.155", "license": "Apache-2.0", "dependencies": { "sass": "1.103.1" diff --git a/docs/releases/web-console-1.6.155.md b/docs/releases/web-console-1.6.155.md new file mode 100644 index 0000000000..3f9fa783c0 --- /dev/null +++ b/docs/releases/web-console-1.6.155.md @@ -0,0 +1,19 @@ +# Web Console 1.6.155 + +Server v1.6.488 packaged Web Console 1.6.154 and correctly denied direct +Stack/Service creation without permission. Browser screenshots at 375×812 and +1440×900 exposed a separate layout defect: the global error notice started at +10px while the 45px top navigation was still underneath it. The message was +technically inside the viewport but visually obscured the navigation. + +The shared growl stylesheet now starts top-right notices below the existing +`$navbar-height` token, retaining the vendor notification margin as the gap. +The notification width is capped to the viewport minus its two 10px margins. +This applies to light/dark themes and LTR/RTL without changing route guards, +API permissions, or notification text. + +The focused Chrome QUnit layout test covers both themes, both directions and +1440px, 375px, and 280px widths. It checks notice/nav separation, viewport +fit, dismiss-control placement and opacity. Local production/development +build and the focused test passed. Official release CI and packaged 8080 +browser acceptance are separate gates and must be recorded after publication. diff --git a/package-lock.json b/package-lock.json index 80871a793c..b106cb37d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@pasturestack/web-console", - "version": "1.6.154", + "version": "1.6.155", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@pasturestack/web-console", - "version": "1.6.154", + "version": "1.6.155", "license": "Apache-2.0", "dependencies": { "sass": "1.103.1" diff --git a/package.json b/package.json index cc193ee983..d1493b989f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@pasturestack/web-console", - "version": "1.6.154", + "version": "1.6.155", "private": true, "description": "PastureStack browser console for the compatible control platform.", "repository": { diff --git a/scripts/check-modernization-blockers b/scripts/check-modernization-blockers index 15b2ae2594..f6d8b9531e 100755 --- a/scripts/check-modernization-blockers +++ b/scripts/check-modernization-blockers @@ -41,8 +41,8 @@ with open('package.json', encoding='utf-8') as f: print(json.load(f).get('version', '')) PY ) -if [[ "$version" != "1.6.154" ]]; then - echo "UNEXPECTED_UI_ARTIFACT_VERSION version=$version expected=1.6.154" +if [[ "$version" != "1.6.155" ]]; then + echo "UNEXPECTED_UI_ARTIFACT_VERSION version=$version expected=1.6.155" failures=$((failures + 1)) fi diff --git a/scripts/check-ui-console-workspace b/scripts/check-ui-console-workspace index b4ce7cf3ea..ea9c19448e 100755 --- a/scripts/check-ui-console-workspace +++ b/scripts/check-ui-console-workspace @@ -141,4 +141,4 @@ if [[ -n ${PASTURESTACK_PRIVATE_MARKER:-} ]] && grep -RInF -- "$PASTURESTACK_PRI fi printf 'UI_CONSOLE_WORKSPACE_OK version=%s persistence=%s cross_tab=%s\n' \ - 1.6.154 browser-session broker-broadcast + 1.6.155 browser-session broker-broadcast diff --git a/scripts/check-ui-critical-high-dependencies b/scripts/check-ui-critical-high-dependencies index e9a6c92917..60ddcc8683 100755 --- a/scripts/check-ui-critical-high-dependencies +++ b/scripts/check-ui-critical-high-dependencies @@ -66,7 +66,7 @@ if lock_bytes != baseline_bytes: lock = json.loads(lock_bytes) packages = lock.get("packages", {}) root = packages.get("", {}) -if package.get("version") != "1.6.154": +if package.get("version") != "1.6.155": fail(f"unexpected Web Console version: {package.get('version')}") if root.get("version") != package.get("version"): fail(f"lock root version differs: {root.get('version')}") diff --git a/scripts/check-ui-sass-import-boundary b/scripts/check-ui-sass-import-boundary index 986fdc653f..a7cc33d4d2 100755 --- a/scripts/check-ui-sass-import-boundary +++ b/scripts/check-ui-sass-import-boundary @@ -105,7 +105,7 @@ configured_load_css_expected = { "app/styles/layout/footer": ['"footer": $footer', '"midGrayDark": $midGrayDark'], "app/styles/layout/layout": ['"accent-two": $accent-two', '"body-bg": $body-bg', '"bold-weight": $bold-weight', '"brand-primary": $brand-primary', '"font-size-small": $font-size-small', '"icon-check": $icon-check', '"lightGrayDark": $lightGrayDark', '"lightGrayTwo": $lightGrayTwo', '"lightestGray": $lightestGray', '"line-height-base": $line-height-base', '"line-height-computed": $line-height-computed', '"main-padding": $main-padding', '"midGray": $midGray', '"normal-weight": $normal-weight', '"orange": $orange', '"page-header-border-color": $page-header-border-color', '"primary": $primary', '"tab-bg": $tab-bg', '"tab-color": $tab-color', '"tab-hover": $tab-hover', '"table-bg-accent": $table-bg-accent', '"table-bg-hover": $table-bg-hover', '"table-border-color": $table-border-color', '"table-cell-padding": $table-cell-padding', '"tealTwo": $tealTwo', '"well-bg": $well-bg', '"well-border": $well-border'], "app/styles/layout/flex-box": ['"table-border-color": $table-border-color'], - "app/styles/components/growl": ['"brand-danger": $brand-danger'], + "app/styles/components/growl": ['"brand-danger": $brand-danger', '"navbar-height": $navbar-height'], "app/styles/components/resource-actions": ['"text-color": $text-color'], "app/styles/components/login": ['"primary": $primary'], "app/styles/components/progress-bar": ['"lightestGray": $lightestGray'], diff --git a/tests/unit/vendor/growl-viewport-layout-test.js b/tests/unit/vendor/growl-viewport-layout-test.js new file mode 100644 index 0000000000..403d9485d2 --- /dev/null +++ b/tests/unit/vendor/growl-viewport-layout-test.js @@ -0,0 +1,61 @@ +import { module, test } from 'qunit'; + +module('Unit | Vendor | Growl viewport layout'); + +function growlFrame(asset, direction) { + return new Promise((resolve, reject) => { + let frame = document.createElement('iframe'); + let timer = setTimeout(() => reject(new Error(`Timed out loading ${asset}`)), 10000); + + frame.style.cssText = 'position:absolute;top:0;left:0;width:1440px;height:600px;opacity:0;pointer-events:none;border:0'; + frame.onload = () => { + clearTimeout(timer); + resolve(frame); + }; + frame.srcdoc = `
+ + + +