diff --git a/ui/admin/app/utils/param-value-finder.js b/addons/core/addon/utils/param-value-finder.js similarity index 100% rename from ui/admin/app/utils/param-value-finder.js rename to addons/core/addon/utils/param-value-finder.js diff --git a/addons/rose/app/components/rose/header.js b/addons/core/app/utils/param-value-finder.js similarity index 58% rename from addons/rose/app/components/rose/header.js rename to addons/core/app/utils/param-value-finder.js index 8a832c21d..32d925f60 100644 --- a/addons/rose/app/components/rose/header.js +++ b/addons/core/app/utils/param-value-finder.js @@ -3,4 +3,4 @@ * SPDX-License-Identifier: BUSL-1.1 */ -export { default } from 'rose/components/rose/header'; +export { default } from 'core/utils/param-value-finder'; diff --git a/addons/core/tests/unit/utils/param-value-finder-test.js b/addons/core/tests/unit/utils/param-value-finder-test.js new file mode 100644 index 000000000..cfdd13106 --- /dev/null +++ b/addons/core/tests/unit/utils/param-value-finder-test.js @@ -0,0 +1,55 @@ +/** + * Copyright IBM Corp. 2021, 2026 + * SPDX-License-Identifier: BUSL-1.1 + */ + +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; +import { paramValueFinder } from 'core/utils/param-value-finder'; + +module('Unit | Utility | param-value-finder', function (hooks) { + setupTest(hooks); + + test('returns empty array when null or empty routeInfo object is used', function (assert) { + let result = paramValueFinder('scopes', null); + + assert.deepEqual(result, []); + + result = paramValueFinder('scopes', {}); + + assert.deepEqual(result, []); + }); + + test('returns correct array when routeInfo object is used', function (assert) { + // Route being tested is '/scopes' + const application = { localName: 'application', params: {}, parent: null }; + const scopes = { localName: 'scopes', params: {}, parent: application }; + const scopesIndex = { + localName: 'index', + name: 'scopes.index', + parent: scopes, + }; + let result = paramValueFinder('scopes', scopesIndex.parent); + + assert.deepEqual(result, []); + + // Route being tested is '/scopes/global/auth-methods' + const scope = { + localName: 'scope', + params: { scope_id: 'global' }, + parent: scopes, + }; + const authMethods = { + localName: 'auth-methods', + params: {}, + parent: scope, + }; + const authMethodsIndex = { + localName: 'index', + name: 'scopes.scope.auth-methods.index', + parent: authMethods, + }; + result = paramValueFinder('scopes', authMethodsIndex.parent); + assert.deepEqual(result, ['global']); + }); +}); diff --git a/addons/rose/addon/components/rose/header/brand/index.hbs b/addons/rose/addon/components/rose/header/brand/index.hbs deleted file mode 100644 index 6fb597932..000000000 --- a/addons/rose/addon/components/rose/header/brand/index.hbs +++ /dev/null @@ -1,24 +0,0 @@ -{{! - Copyright IBM Corp. 2021, 2026 - SPDX-License-Identifier: BUSL-1.1 -}} - - - - {{#if @logo}} - {{inline-svg @logo}} - {{/if}} - - {{#if @text}} - {{@text}} - {{/if}} - - \ No newline at end of file diff --git a/addons/rose/addon/components/rose/header/index.hbs b/addons/rose/addon/components/rose/header/index.hbs deleted file mode 100644 index 792a328e4..000000000 --- a/addons/rose/addon/components/rose/header/index.hbs +++ /dev/null @@ -1,14 +0,0 @@ -{{! - Copyright IBM Corp. 2021, 2026 - SPDX-License-Identifier: BUSL-1.1 -}} - -
- {{yield - (hash - brand=(component 'rose/header/brand') - utilities=(component 'rose/header/utilities') - nav=(component 'rose/header/nav') - ) - }} -
\ No newline at end of file diff --git a/addons/rose/addon/components/rose/header/nav/index.hbs b/addons/rose/addon/components/rose/header/nav/index.hbs deleted file mode 100644 index c5f088b82..000000000 --- a/addons/rose/addon/components/rose/header/nav/index.hbs +++ /dev/null @@ -1,8 +0,0 @@ -{{! - Copyright IBM Corp. 2021, 2026 - SPDX-License-Identifier: BUSL-1.1 -}} - - \ No newline at end of file diff --git a/addons/rose/addon/components/rose/header/utilities/index.hbs b/addons/rose/addon/components/rose/header/utilities/index.hbs deleted file mode 100644 index 9f3f129c5..000000000 --- a/addons/rose/addon/components/rose/header/utilities/index.hbs +++ /dev/null @@ -1,8 +0,0 @@ -{{! - Copyright IBM Corp. 2021, 2026 - SPDX-License-Identifier: BUSL-1.1 -}} - -
- {{yield}} -
\ No newline at end of file diff --git a/addons/rose/addon/components/rose/layout/global/index.hbs b/addons/rose/addon/components/rose/layout/global/index.hbs deleted file mode 100644 index 0e78abef6..000000000 --- a/addons/rose/addon/components/rose/layout/global/index.hbs +++ /dev/null @@ -1,16 +0,0 @@ -{{! - Copyright IBM Corp. 2021, 2026 - SPDX-License-Identifier: BUSL-1.1 -}} - -
- {{yield - (hash - header=(component - 'rose/anonymous' tagName='header' class='rose-layout-global-header' - ) - body=(component 'rose/anonymous' class='rose-layout-global-body') - footer=(component 'rose/anonymous' class='rose-layout-global-footer') - ) - }} -
\ No newline at end of file diff --git a/addons/rose/addon/styles/hds/overrides.scss b/addons/rose/addon/styles/hds/overrides.scss index 9420afaff..466db6ca9 100644 --- a/addons/rose/addon/styles/hds/overrides.scss +++ b/addons/rose/addon/styles/hds/overrides.scss @@ -47,41 +47,8 @@ form { } } -// TODO: This is a temporary hack to fix an issue causing weird whitespace -// and scrollbars from an accessible class in HDS. We have to use !important -// as they already forced it with !important and we can't override it otherwise -.sr-only { - position: unset !important; -} - .hds-dropdown { .hds-dropdown-toggle-icon { color: inherit; } } - -// TODO: This is a temporary fix that ensures changing the color theme won't alter the dropdown colors. -.app-frame-sidebar-style-overrides .side-nav-style-overrides { - .hds-button:not( - .hds-dropdown__content *, - .ember-basic-dropdown-content *, - .hds-dialog-primitive__wrapper * - ), - button[class*='hds-dropdown-toggle']:not( - .hds-dropdown__content *, - .ember-basic-dropdown-content *, - .hds-dialog-primitive__wrapper * - ) { - background: var(--token-side-nav-color-surface-primary); - border-color: var(--token-app-side-nav-color-foreground-faint); - - &:hover { - background: var(--token-side-nav-color-surface-interactive-hover); - } - - &:active, - &:focus { - background: var(--token-side-nav-color-surface-primary); - } - } -} diff --git a/addons/rose/addon/styles/hds/themes/dark-mode/_color-tokens-tier-2.scss b/addons/rose/addon/styles/hds/themes/dark-mode/_color-tokens-tier-2.scss index f12da04e8..30e48ee8c 100644 --- a/addons/rose/addon/styles/hds/themes/dark-mode/_color-tokens-tier-2.scss +++ b/addons/rose/addon/styles/hds/themes/dark-mode/_color-tokens-tier-2.scss @@ -259,4 +259,27 @@ --token-form-toggle-base-surface-color-default: var( --token-color-palette-neutral-100 ); /* the toggle has a different base surface color, compared to the other controls */ + + // Hds app side nav color-mappings + --token-app-side-nav-color-foreground-primary: var( + --token-color-palette-neutral-600 + ); + --token-app-side-nav-color-surface-primary: var( + --token-color-palette-neutral-0 + ); + --token-app-side-nav-wrapper-border-color: var( + --token-color-palette-neutral-200 + ); + --token-app-side-nav-color-foreground-strong: var( + --token-color-palette-neutral-600 + ); + --token-app-side-nav-color-foreground-faint: var( + --token-color-palette-neutral-500 + ); + --token-app-side-nav-color-surface-interactive-hover: var( + --token-color-palette-neutral-50 + ); + --token-app-side-nav-color-surface-interactive-active: var( + --token-color-palette-neutral-100 + ); } diff --git a/addons/rose/addon/styles/hds/themes/dark-mode/index.scss b/addons/rose/addon/styles/hds/themes/dark-mode/index.scss index 824c75ada..c8e939d2f 100644 --- a/addons/rose/addon/styles/hds/themes/dark-mode/index.scss +++ b/addons/rose/addon/styles/hds/themes/dark-mode/index.scss @@ -132,11 +132,86 @@ } } +@mixin dark-mode-button-override { + /* stylelint-disable selector-class-pattern */ + .hds-app-frame:has( + .hds-app-side-nav--is-mobile.hds-app-side-nav--is-not-minimized + ) { + .hds-button, + .hds-dropdown-toggle-button, + .hds-dropdown-toggle-icon, + .hds-app-header__home-link { + color: var(--token-color-palette-neutral-200); + background-color: var(--token-color-palette-neutral-50); + border-color: var(--token-color-palette-neutral-100); + } + } + /* stylelint-enable selector-class-pattern */ +} + +@mixin dark-mode-app-header-override { + .app-header-dark-mode-overrides { + color: var(--token-color-palette-neutral-700); + background: var(--token-color-palette-neutral-0); + border-bottom-color: var(--token-color-palette-neutral-100); + + /* stylelint-disable selector-class-pattern */ + .home-link-color-override, + .hds-app-header__actions { + background-color: var(--token-color-palette-neutral-0); + } + + .dropdown-toggle-color-override, + .hds-app-header__menu-button { + background: var(--token-color-palette-neutral-0); + border-color: var(--token-color-palette-neutral-100); + + &:hover { + background: var(--token-color-palette-neutral-50); + } + + &:active, + &:focus { + background: var(--token-color-palette-neutral-0); + } + } + /* stylelint-enable selector-class-pattern */ + } +} + +@mixin dark-mode-app-side-nav-override { + .side-nav-dark-mode-overrides { + /* stylelint-disable selector-class-pattern */ + .hds-app-side-nav__overlay { + background-color: var(--token-color-palette-neutral-50); + } + + .hds-app-side-nav__list-title { + color: var(--token-color-palette-neutral-300); + } + + .hds-app-side-nav__list-item-link.active { + &::after { + background-color: var(--token-color-palette-blue-400); + } + + .hds-app-side-nav__list-item-icon-leading, + .hds-app-side-nav__list-item-text { + color: var(--token-color-palette-blue-400); + } + } + /* stylelint-enable selector-class-pattern */ + } +} + @media (prefers-color-scheme: dark) { .ember-application:not(.rose-theme-light) { @include dark-mode; @include dark-mode-modal-override; @include dark-mode-ember-dropdown-override; + @include dark-mode-app-header-override; + @include dark-mode-app-side-nav-override; + @include dark-mode-button-override; } } @@ -144,4 +219,7 @@ @include dark-mode; @include dark-mode-modal-override; @include dark-mode-ember-dropdown-override; + @include dark-mode-app-header-override; + @include dark-mode-app-side-nav-override; + @include dark-mode-button-override; } diff --git a/addons/rose/app/components/rose/header/brand.js b/addons/rose/app/components/rose/header/brand.js deleted file mode 100644 index 556b0163a..000000000 --- a/addons/rose/app/components/rose/header/brand.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Copyright IBM Corp. 2021, 2026 - * SPDX-License-Identifier: BUSL-1.1 - */ - -export { default } from 'rose/components/rose/header/brand'; diff --git a/addons/rose/app/components/rose/header/nav.js b/addons/rose/app/components/rose/header/nav.js deleted file mode 100644 index f0d6bab20..000000000 --- a/addons/rose/app/components/rose/header/nav.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Copyright IBM Corp. 2021, 2026 - * SPDX-License-Identifier: BUSL-1.1 - */ - -export { default } from 'rose/components/rose/header/nav'; diff --git a/addons/rose/app/components/rose/header/utilities.js b/addons/rose/app/components/rose/header/utilities.js deleted file mode 100644 index 3206cbb2e..000000000 --- a/addons/rose/app/components/rose/header/utilities.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Copyright IBM Corp. 2021, 2026 - * SPDX-License-Identifier: BUSL-1.1 - */ - -export { default } from 'rose/components/rose/header/utilities'; diff --git a/addons/rose/app/components/rose/layout/global.js b/addons/rose/app/components/rose/layout/global.js deleted file mode 100644 index 9ae36c39c..000000000 --- a/addons/rose/app/components/rose/layout/global.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Copyright IBM Corp. 2021, 2026 - * SPDX-License-Identifier: BUSL-1.1 - */ - -export { default } from 'rose/components/rose/layout/global'; diff --git a/addons/rose/app/styles/rose/components/_index.scss b/addons/rose/app/styles/rose/components/_index.scss index 6f93e0281..d0448dc46 100644 --- a/addons/rose/app/styles/rose/components/_index.scss +++ b/addons/rose/app/styles/rose/components/_index.scss @@ -8,7 +8,6 @@ @use 'code-editor'; @use 'form'; @use 'frame'; -@use 'header'; @use 'layout'; @use 'list'; @use 'metadata-list'; diff --git a/addons/rose/app/styles/rose/components/header/_brand.scss b/addons/rose/app/styles/rose/components/header/_brand.scss deleted file mode 100644 index 35eaa80f2..000000000 --- a/addons/rose/app/styles/rose/components/header/_brand.scss +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright IBM Corp. 2021, 2026 - * SPDX-License-Identifier: BUSL-1.1 - */ - -@use '../../variables/sizing'; -@use '../../utilities/type'; - -.rose-header-brand { - grid-area: brand; - display: flex; - align-items: center; - color: var(--white); - text-decoration: none; - padding: 0 10px; - margin: 10px; - - svg { - display: block; - } - - .rose-header-brand-text { - @include type.type(m); - - font-weight: var(--token-typography-font-weight-regular); - margin: 0; - margin-left: sizing.rems(s); - white-space: nowrap; - position: absolute; - top: -1000px; - left: -1000px; - opacity: 0; - width: 1px; - height: 1px; - } -} diff --git a/addons/rose/app/styles/rose/components/header/_dropdown.scss b/addons/rose/app/styles/rose/components/header/_dropdown.scss deleted file mode 100644 index f7ca2642a..000000000 --- a/addons/rose/app/styles/rose/components/header/_dropdown.scss +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright IBM Corp. 2021, 2026 - * SPDX-License-Identifier: BUSL-1.1 - */ - -.rose-header { - // Temporary fix: This header component will be retired upon integrating HDS side nav for desktop. - .header-dropdown-button-override > button { - background: var(--black); - border: none; - color: var(--white); - } -} diff --git a/addons/rose/app/styles/rose/components/header/_index.scss b/addons/rose/app/styles/rose/components/header/_index.scss deleted file mode 100644 index 88b428b6b..000000000 --- a/addons/rose/app/styles/rose/components/header/_index.scss +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright IBM Corp. 2021, 2026 - * SPDX-License-Identifier: BUSL-1.1 - */ - -@use '../../variables/sizing'; -@use '../../variables/media'; -@use '../../utilities/type'; - -@use 'brand'; -@use 'nav'; -@use 'utilities'; -@use 'dropdown'; - -.rose-header { - --box-shadow-light: none; - --box-shadow-dark: inset 0 -#{sizing.rems(xxxxs)} 0 var(--ui-border-subtler-1); - --box-shadow: var(--box-shadow-light); - - display: grid; - grid-template-areas: 'brand nav utilities'; - grid-template-columns: 0.01fr auto 1fr; - background: var(--black); - box-shadow: var(--box-shadow); - - .ember-application.rose-theme-light & { - --box-shadow: var(--box-shadow-light); - } - - @media (prefers-color-scheme: dark) { - --box-shadow: var(--box-shadow-dark); - } - - .ember-application.rose-theme-dark & { - --box-shadow: var(--box-shadow-dark); - } -} - -@media (width <= (media.width(small))) { - .rose-header { - grid-template-columns: 1fr 3fr; - grid-template-areas: - 'brand utilities' - 'nav nav'; - padding-bottom: 0; - } -} diff --git a/addons/rose/app/styles/rose/components/header/_nav.scss b/addons/rose/app/styles/rose/components/header/_nav.scss deleted file mode 100644 index a7e8de98e..000000000 --- a/addons/rose/app/styles/rose/components/header/_nav.scss +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright IBM Corp. 2021, 2026 - * SPDX-License-Identifier: BUSL-1.1 - */ - -@use '../../variables/sizing'; -@use '../../variables/media'; - -.rose-header-nav { - grid-area: nav; - display: flex; - flex-wrap: wrap; - align-items: center; - margin-right: sizing.rems(s); - - @media (width <= (media.width(small))) { - padding: 0 20px; - background: var(--ui-header-subtler); - } -} diff --git a/addons/rose/app/styles/rose/components/header/_utilities.scss b/addons/rose/app/styles/rose/components/header/_utilities.scss deleted file mode 100644 index 3d318d4af..000000000 --- a/addons/rose/app/styles/rose/components/header/_utilities.scss +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright IBM Corp. 2021, 2026 - * SPDX-License-Identifier: BUSL-1.1 - */ - -@use '../../variables/sizing'; - -.rose-header-utilities { - grid-area: utilities; - display: flex; - flex-wrap: wrap; - flex-direction: row-reverse; - align-items: center; - margin-right: sizing.rems(s); -} diff --git a/addons/rose/app/styles/rose/components/layout/_global.scss b/addons/rose/app/styles/rose/components/layout/_global.scss deleted file mode 100644 index 392e963d7..000000000 --- a/addons/rose/app/styles/rose/components/layout/_global.scss +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright IBM Corp. 2021, 2026 - * SPDX-License-Identifier: BUSL-1.1 - */ - -.rose-layout-global { - min-height: 100vh; - display: flex; - flex-direction: column; - - .rose-layout-global-body { - flex: 1; - display: flex; - } -} diff --git a/addons/rose/app/styles/rose/components/layout/_index.scss b/addons/rose/app/styles/rose/components/layout/_index.scss index 1f46c8ad6..3dd429763 100644 --- a/addons/rose/app/styles/rose/components/layout/_index.scss +++ b/addons/rose/app/styles/rose/components/layout/_index.scss @@ -3,7 +3,6 @@ * SPDX-License-Identifier: BUSL-1.1 */ -@use 'global'; @use 'sidebar'; @use 'page'; @use 'centered'; diff --git a/addons/rose/app/styles/rose/components/layout/_page.scss b/addons/rose/app/styles/rose/components/layout/_page.scss index 4c790b0bb..367d97bfd 100644 --- a/addons/rose/app/styles/rose/components/layout/_page.scss +++ b/addons/rose/app/styles/rose/components/layout/_page.scss @@ -18,6 +18,7 @@ .rose-layout-page-header { grid-area: header; margin-bottom: sizing.rems(s); + margin-top: sizing.rems(l); h1 { display: flex; diff --git a/addons/rose/tests/integration/components/rose/header-test.js b/addons/rose/tests/integration/components/rose/header-test.js deleted file mode 100644 index fe47f5ed9..000000000 --- a/addons/rose/tests/integration/components/rose/header-test.js +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright IBM Corp. 2021, 2026 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'ember-qunit'; -import { render, find } from '@ember/test-helpers'; -import { hbs } from 'ember-cli-htmlbars'; - -module('Integration | Component | rose/header', function (hooks) { - setupRenderingTest(hooks); - - test('it renders', async function (assert) { - await render(hbs``); - assert.ok(find('.rose-header')); - }); - - test('it renders with brand', async function (assert) { - await render(hbs` - - `); - assert.ok(find('.rose-header-brand')); - assert.notOk(find('.rose-header-nav')); - assert.notOk(find('.rose-header-utilities')); - }); - - test('it renders with nav', async function (assert) { - await render(hbs` - - `); - assert.ok(find('.rose-header-nav')); - assert.notOk(find('.rose-header-brand')); - assert.notOk(find('.rose-header-utilities')); - }); - - test('it renders with utilities', async function (assert) { - await render(hbs` - - `); - assert.notOk(find('.rose-header-brand')); - assert.notOk(find('.rose-header-nav')); - assert.ok(find('.rose-header-utilities')); - }); - - test('it renders the hds dropdown within header utilities', async function (assert) { - await render(hbs` - - - - - - `); - assert.ok(find('.hds-dropdown')); - }); -}); diff --git a/addons/rose/tests/integration/components/rose/header/brand-test.js b/addons/rose/tests/integration/components/rose/header/brand-test.js deleted file mode 100644 index 066f78901..000000000 --- a/addons/rose/tests/integration/components/rose/header/brand-test.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright IBM Corp. 2021, 2026 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'ember-qunit'; -import { render, find } from '@ember/test-helpers'; -import { hbs } from 'ember-cli-htmlbars'; - -module('Integration | Component | rose/header/brand', function (hooks) { - setupRenderingTest(hooks); - - test('it renders', async function (assert) { - await render(hbs` - - `); - assert.ok(find('.rose-header-brand')); - assert.ok(find('.rose-header-brand svg')); - assert.strictEqual( - find('.rose-header-brand-text').textContent.trim(), - 'Product Name', - ); - }); -}); diff --git a/addons/rose/tests/integration/components/rose/header/nav-test.js b/addons/rose/tests/integration/components/rose/header/nav-test.js deleted file mode 100644 index fecc82706..000000000 --- a/addons/rose/tests/integration/components/rose/header/nav-test.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright IBM Corp. 2021, 2026 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'ember-qunit'; -import { render, find } from '@ember/test-helpers'; -import { hbs } from 'ember-cli-htmlbars'; - -module('Integration | Component | rose/header/nav', function (hooks) { - setupRenderingTest(hooks); - - test('it renders', async function (assert) { - await render(hbs``); - assert.ok(find('.rose-header-nav')); - }); -}); diff --git a/addons/rose/tests/integration/components/rose/header/utilities-test.js b/addons/rose/tests/integration/components/rose/header/utilities-test.js deleted file mode 100644 index 6cf670f4c..000000000 --- a/addons/rose/tests/integration/components/rose/header/utilities-test.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright IBM Corp. 2021, 2026 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'ember-qunit'; -import { render, find } from '@ember/test-helpers'; -import { hbs } from 'ember-cli-htmlbars'; - -module('Integration | Component | rose/header/utilities', function (hooks) { - setupRenderingTest(hooks); - - test('it renders', async function (assert) { - await render(hbs``); - assert.ok(find('.rose-header-utilities')); - }); -}); diff --git a/addons/rose/tests/integration/components/rose/layout/global-test.js b/addons/rose/tests/integration/components/rose/layout/global-test.js deleted file mode 100644 index 978f2f898..000000000 --- a/addons/rose/tests/integration/components/rose/layout/global-test.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright IBM Corp. 2021, 2026 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'ember-qunit'; -import { render, find } from '@ember/test-helpers'; -import { hbs } from 'ember-cli-htmlbars'; - -module('Integration | Component | rose/layout/global', function (hooks) { - setupRenderingTest(hooks); - - test('it renders', async function (assert) { - await render(hbs``); - assert.ok(find('section')); - assert.ok(find('.rose-layout-global')); - }); - - test('it renders with attributes', async function (assert) { - await render(hbs``); - assert.ok(find('#global')); - }); - - test('it renders with header', async function (assert) { - await render(hbs` - - `); - assert.ok(find('.rose-layout-global-header')); - }); - - test('it renders with body', async function (assert) { - await render(hbs` - - `); - assert.ok(find('.rose-layout-global-body')); - }); - - test('it renders with footer', async function (assert) { - await render(hbs` - - `); - assert.ok(find('.rose-layout-global-footer')); - }); -}); diff --git a/e2e-tests/admin/pages/auth-methods.js b/e2e-tests/admin/pages/auth-methods.js index ee61f7b1b..41521d417 100644 --- a/e2e-tests/admin/pages/auth-methods.js +++ b/e2e-tests/admin/pages/auth-methods.js @@ -16,7 +16,7 @@ export class AuthMethodsPage extends BaseResourcePage { async createPasswordAuthMethod() { const authMethodName = 'Auth Method ' + nanoid(); await this.page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Auth Methods' }) .click(); await this.page.getByRole('button', { name: 'New', exact: true }).click(); @@ -43,7 +43,7 @@ export class AuthMethodsPage extends BaseResourcePage { async createOidcAuthMethod(issuer, clientId, clientSecret, boundaryAddr) { const authMethodName = 'Auth Method ' + nanoid(); await this.page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Auth Methods' }) .click(); await this.page.getByRole('button', { name: 'New', exact: true }).click(); @@ -130,7 +130,7 @@ export class AuthMethodsPage extends BaseResourcePage { */ async makeAuthMethodPrimary() { await this.page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Auth Methods' }) .click(); await this.page.getByLabel('Manage').click(); diff --git a/e2e-tests/admin/pages/credential-stores.js b/e2e-tests/admin/pages/credential-stores.js index c5fe3e89c..b74bfd5b6 100644 --- a/e2e-tests/admin/pages/credential-stores.js +++ b/e2e-tests/admin/pages/credential-stores.js @@ -17,7 +17,7 @@ export class CredentialStoresPage extends BaseResourcePage { async createStaticCredentialStore() { const credentialStoreName = 'Credential Store ' + nanoid(); await this.page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Credential Stores' }) .click(); await this.page.getByRole('link', { name: 'New', exact: true }).click(); @@ -47,7 +47,7 @@ export class CredentialStoresPage extends BaseResourcePage { async createVaultCredentialStore(vaultAddr, clientToken) { const credentialStoreName = 'Credential Store ' + nanoid(); await this.page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Credential Stores' }) .click(); await this.page.getByRole('link', { name: 'New', exact: true }).click(); @@ -84,7 +84,7 @@ export class CredentialStoresPage extends BaseResourcePage { ) { const credentialStoreName = 'Credential Store ' + nanoid(); await this.page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Credential Stores' }) .click(); await this.page.getByRole('link', { name: 'New', exact: true }).click(); diff --git a/e2e-tests/admin/pages/groups.js b/e2e-tests/admin/pages/groups.js index 4af8f8b7c..581d5985a 100644 --- a/e2e-tests/admin/pages/groups.js +++ b/e2e-tests/admin/pages/groups.js @@ -16,7 +16,7 @@ export class GroupsPage extends BaseResourcePage { async createGroup() { const groupName = 'Group ' + nanoid(); await this.page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Groups' }) .click(); await this.page.getByRole('link', { name: 'New', exact: true }).click(); diff --git a/e2e-tests/admin/pages/host-catalogs.js b/e2e-tests/admin/pages/host-catalogs.js index 52583b62c..aafbba547 100644 --- a/e2e-tests/admin/pages/host-catalogs.js +++ b/e2e-tests/admin/pages/host-catalogs.js @@ -16,7 +16,7 @@ export class HostCatalogsPage extends BaseResourcePage { async createHostCatalog() { const hostCatalogName = 'Host Catalog ' + nanoid(); await this.page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Host Catalogs' }) .click(); await expect( diff --git a/e2e-tests/admin/pages/login.js b/e2e-tests/admin/pages/login.js index afd1bdc86..3d54f047e 100644 --- a/e2e-tests/admin/pages/login.js +++ b/e2e-tests/admin/pages/login.js @@ -18,9 +18,7 @@ export class LoginPage extends BaseResourcePage { await this.page.getByLabel('Password', { exact: true }).fill(password); await this.page.getByRole('button', { name: 'Sign In' }).click(); await expect( - this.page.getByRole('navigation', { - name: 'Application local navigation', - }), + this.page.getByRole('navigation', { name: 'Primary' }), ).toBeVisible(); await expect(this.page.getByText(loginName)).toBeEnabled(); } diff --git a/e2e-tests/admin/pages/orgs.js b/e2e-tests/admin/pages/orgs.js index 3b7e3f247..212bf47b7 100644 --- a/e2e-tests/admin/pages/orgs.js +++ b/e2e-tests/admin/pages/orgs.js @@ -16,7 +16,7 @@ export class OrgsPage extends BaseResourcePage { async createOrg() { const orgName = 'Org ' + nanoid(); await this.page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Orgs' }) .click(); await this.page.getByRole('link', { name: 'New Org' }).click(); diff --git a/e2e-tests/admin/pages/projects.js b/e2e-tests/admin/pages/projects.js index 091177641..7215b52eb 100644 --- a/e2e-tests/admin/pages/projects.js +++ b/e2e-tests/admin/pages/projects.js @@ -16,7 +16,7 @@ export class ProjectsPage extends BaseResourcePage { async createProject() { const projectName = 'Project ' + nanoid(); await this.page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Projects' }) .click(); await this.page.getByRole('link', { name: 'New', exact: true }).click(); diff --git a/e2e-tests/admin/pages/roles.js b/e2e-tests/admin/pages/roles.js index 7eb8ac4e1..47285cff6 100644 --- a/e2e-tests/admin/pages/roles.js +++ b/e2e-tests/admin/pages/roles.js @@ -21,7 +21,7 @@ export class RolesPage extends BaseResourcePage { const roleName = 'Role ' + nanoid(); await this.page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Roles' }) .click(); await this.page.getByRole('link', { name: 'New Role' }).click(); diff --git a/e2e-tests/admin/pages/sessions.js b/e2e-tests/admin/pages/sessions.js index 5c4103541..e88148a58 100644 --- a/e2e-tests/admin/pages/sessions.js +++ b/e2e-tests/admin/pages/sessions.js @@ -14,7 +14,7 @@ export class SessionsPage extends BasePage { */ async waitForSessionToBeVisible(targetName) { await this.page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Sessions' }) .click(); let i = 0; diff --git a/e2e-tests/admin/pages/targets.js b/e2e-tests/admin/pages/targets.js index 5350b8723..597400aca 100644 --- a/e2e-tests/admin/pages/targets.js +++ b/e2e-tests/admin/pages/targets.js @@ -37,7 +37,7 @@ export class TargetsPage extends BaseResourcePage { } await this.page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Targets' }) .click(); await this.page.getByRole('link', { name: 'New', exact: true }).click(); @@ -187,7 +187,7 @@ export class TargetsPage extends BaseResourcePage { */ async addBrokeredCredentialsToTarget(targetName, credentialName) { await this.page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Targets' }) .click(); await expect( @@ -235,7 +235,7 @@ export class TargetsPage extends BaseResourcePage { */ async addInjectedCredentialsToTarget(targetName, credentialName) { await this.page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Targets' }) .click(); await expect( diff --git a/e2e-tests/admin/pages/users.js b/e2e-tests/admin/pages/users.js index f4b04d1f8..a4236359e 100644 --- a/e2e-tests/admin/pages/users.js +++ b/e2e-tests/admin/pages/users.js @@ -16,7 +16,7 @@ export class UsersPage extends BaseResourcePage { async createUser() { const userName = 'User ' + nanoid(); await this.page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Users' }) .click(); await this.page.getByRole('link', { name: 'New', exact: true }).click(); diff --git a/e2e-tests/admin/tests/alias-ent.spec.js b/e2e-tests/admin/tests/alias-ent.spec.js index 8674f1737..4b3c0973b 100644 --- a/e2e-tests/admin/tests/alias-ent.spec.js +++ b/e2e-tests/admin/tests/alias-ent.spec.js @@ -93,7 +93,7 @@ test.describe('Aliases (Enterprise)', () => { // Clear destination from alias await page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Targets' }) .click(); await page.getByRole('link', { name: targetName }).click(); @@ -285,7 +285,7 @@ test.describe('Aliases (Enterprise)', () => { const aliasesPage = new AliasesPage(page); await aliasesPage.createAliasForTarget(alias, targetId); await page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Orgs' }) .click(); await page.getByRole('link', { name: orgName }).click(); diff --git a/e2e-tests/admin/tests/alias.spec.js b/e2e-tests/admin/tests/alias.spec.js index 1ef85526a..8de70ed04 100644 --- a/e2e-tests/admin/tests/alias.spec.js +++ b/e2e-tests/admin/tests/alias.spec.js @@ -75,7 +75,7 @@ test.describe('Aliases', () => { // Clear destination from alias await page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Targets' }) .click(); await page.getByRole('link', { name: targetName }).click(); diff --git a/e2e-tests/admin/tests/auth-method-ldap.spec.js b/e2e-tests/admin/tests/auth-method-ldap.spec.js index 250c27167..4af501e45 100644 --- a/e2e-tests/admin/tests/auth-method-ldap.spec.js +++ b/e2e-tests/admin/tests/auth-method-ldap.spec.js @@ -52,7 +52,7 @@ test( const orgsPage = new OrgsPage(page); orgName = await orgsPage.createOrg(); await page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Auth Methods' }) .click(); await expect( @@ -194,7 +194,7 @@ test( // View the LDAP account and verify account attributes await page.getByRole('link', { name: orgName }).click(); await page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Auth Methods' }) .click(); await page.getByRole('link', { name: ldapAuthMethodName }).click(); @@ -283,7 +283,7 @@ test( // View the User account and verify attributes await page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Users' }) .click(); await page.getByRole('link', { name: userName }).click(); diff --git a/e2e-tests/admin/tests/auth-method-oidc-vault.spec.js b/e2e-tests/admin/tests/auth-method-oidc-vault.spec.js index f113c7406..82c3e8437 100644 --- a/e2e-tests/admin/tests/auth-method-oidc-vault.spec.js +++ b/e2e-tests/admin/tests/auth-method-oidc-vault.spec.js @@ -147,7 +147,7 @@ test( .getByText(orgName), ).toBeVisible(); await page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Auth Methods' }) .click(); await page.getByRole('link', { name: oidcAuthMethodName }).click(); @@ -236,7 +236,7 @@ test( // View the User account and verify attributes await page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Users' }) .click(); await page diff --git a/e2e-tests/admin/tests/auth-method-password.spec.js b/e2e-tests/admin/tests/auth-method-password.spec.js index c6fde39fb..31a15814d 100644 --- a/e2e-tests/admin/tests/auth-method-password.spec.js +++ b/e2e-tests/admin/tests/auth-method-password.spec.js @@ -75,7 +75,7 @@ test( // Set a new password on the account await page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Orgs' }) .click(); await page.getByRole('link', { name: orgName }).click(); @@ -85,7 +85,7 @@ test( .getByText(orgName), ).toBeVisible(); await page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Auth Methods' }) .click(); await page.getByRole('link', { name: authMethodName }).click(); diff --git a/e2e-tests/admin/tests/dynamic-host-catalog-aws-ent.spec.js b/e2e-tests/admin/tests/dynamic-host-catalog-aws-ent.spec.js index 2f33d663c..dc6d139a1 100644 --- a/e2e-tests/admin/tests/dynamic-host-catalog-aws-ent.spec.js +++ b/e2e-tests/admin/tests/dynamic-host-catalog-aws-ent.spec.js @@ -49,7 +49,7 @@ test.describe('AWS', () => { // Create host catalog const hostCatalogName = 'Host Catalog ' + nanoid(); await page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Host Catalogs' }) .click(); await page.getByRole('link', { name: 'New', exact: true }).click(); @@ -167,7 +167,7 @@ test.describe('AWS', () => { await hostCatalogsPage.createHostCatalog(); const newHostSetName = await hostCatalogsPage.createHostSet(); await page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Targets' }) .click(); await page.getByRole('link', { name: targetName }).click(); diff --git a/e2e-tests/admin/tests/dynamic-host-catalog-aws.spec.js b/e2e-tests/admin/tests/dynamic-host-catalog-aws.spec.js index 2264ba3d5..de11a9699 100644 --- a/e2e-tests/admin/tests/dynamic-host-catalog-aws.spec.js +++ b/e2e-tests/admin/tests/dynamic-host-catalog-aws.spec.js @@ -49,7 +49,7 @@ test.describe('AWS', () => { // Create host catalog const hostCatalogName = 'Host Catalog ' + nanoid(); await page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Host Catalogs' }) .click(); await page.getByRole('link', { name: 'New', exact: true }).click(); @@ -166,7 +166,7 @@ test.describe('AWS', () => { await hostCatalogsPage.createHostCatalog(); const newHostSetName = await hostCatalogsPage.createHostSet(); await page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Targets' }) .click(); await page.getByRole('link', { name: targetName }).click(); diff --git a/e2e-tests/admin/tests/global-settings.spec.js b/e2e-tests/admin/tests/global-settings.spec.js index 09944eb39..1f445333f 100644 --- a/e2e-tests/admin/tests/global-settings.spec.js +++ b/e2e-tests/admin/tests/global-settings.spec.js @@ -42,7 +42,7 @@ test( // Navigate to orgs page when done await page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Orgs' }) .click(); await expect(page.getByRole('heading', { name: 'Orgs' })).toBeVisible(); diff --git a/e2e-tests/admin/tests/pagination.spec.js b/e2e-tests/admin/tests/pagination.spec.js index 70fb7c1c7..a97f22459 100644 --- a/e2e-tests/admin/tests/pagination.spec.js +++ b/e2e-tests/admin/tests/pagination.spec.js @@ -48,7 +48,7 @@ test( await page.getByRole('link', { name: org.name }).click(); await page.getByRole('link', { name: project.name }).click(); await page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Targets' }) .click(); await expect(page.getByRole('heading', { name: 'Targets' })).toBeVisible(); diff --git a/e2e-tests/admin/tests/session-recording-aws-ent.spec.js b/e2e-tests/admin/tests/session-recording-aws-ent.spec.js index 01b5a2f8f..9cf6c32bf 100644 --- a/e2e-tests/admin/tests/session-recording-aws-ent.spec.js +++ b/e2e-tests/admin/tests/session-recording-aws-ent.spec.js @@ -160,7 +160,7 @@ test( .getByRole('link', { name: 'Back to Global', exact: true }) .click(); await page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Session Recordings', exact: true }) .click(); await page diff --git a/e2e-tests/admin/tests/session-recording-minio-ent.spec.js b/e2e-tests/admin/tests/session-recording-minio-ent.spec.js index 280214fc0..a1d2e2edc 100644 --- a/e2e-tests/admin/tests/session-recording-minio-ent.spec.js +++ b/e2e-tests/admin/tests/session-recording-minio-ent.spec.js @@ -168,7 +168,7 @@ test( .getByRole('link', { name: 'Back to Global', exact: true }) .click(); await page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Session Recordings', exact: true }) .click(); await page diff --git a/e2e-tests/admin/tests/target.spec.js b/e2e-tests/admin/tests/target.spec.js index ea34b548c..d59291340 100644 --- a/e2e-tests/admin/tests/target.spec.js +++ b/e2e-tests/admin/tests/target.spec.js @@ -49,7 +49,7 @@ test( // Create another host set await page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Host Catalogs' }) .click(); await page.getByRole('link', { name: hostCatalogName }).click(); diff --git a/e2e-tests/admin/tests/worker-ent.spec.js b/e2e-tests/admin/tests/worker-ent.spec.js index 6316db9c7..a43fa7de5 100644 --- a/e2e-tests/admin/tests/worker-ent.spec.js +++ b/e2e-tests/admin/tests/worker-ent.spec.js @@ -14,7 +14,7 @@ test( await page.goto('/'); await page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Workers' }) .click(); await page.getByRole('link', { name: 'New', exact: true }).click(); diff --git a/e2e-tests/admin/tests/worker-tags.spec.js b/e2e-tests/admin/tests/worker-tags.spec.js index 36ddf8acc..41767f1a0 100644 --- a/e2e-tests/admin/tests/worker-tags.spec.js +++ b/e2e-tests/admin/tests/worker-tags.spec.js @@ -17,7 +17,7 @@ test( await page.goto('/'); await page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Workers' }) .click(); await expect( @@ -102,7 +102,7 @@ test( // Check tag in worker dialog await page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Workers' }) .click(); await expect( diff --git a/e2e-tests/admin/tests/worker.spec.js b/e2e-tests/admin/tests/worker.spec.js index 67daf619c..c9c92828e 100644 --- a/e2e-tests/admin/tests/worker.spec.js +++ b/e2e-tests/admin/tests/worker.spec.js @@ -14,7 +14,7 @@ test( await page.goto('/'); await page - .getByRole('navigation', { name: 'Application local navigation' }) + .getByRole('navigation', { name: 'Primary' }) .getByRole('link', { name: 'Workers' }) .click(); await page.getByRole('link', { name: 'New', exact: true }).click(); diff --git a/e2e-tests/desktop/tests/scope.spec.js b/e2e-tests/desktop/tests/scope.spec.js index dfe52b368..cc6c2e3f6 100644 --- a/e2e-tests/desktop/tests/scope.spec.js +++ b/e2e-tests/desktop/tests/scope.spec.js @@ -50,7 +50,9 @@ test.describe('Scope tests', () => { test('Shows the filtered targets based on selected scope', async ({ authedPage, }) => { - const headerNavLocator = authedPage.getByLabel('header-nav'); + const headerNavLocator = authedPage.locator( + 'header .app-header__global-actions', + ); await expect(headerNavLocator).toBeVisible(); await expect( headerNavLocator.locator('.hds-dropdown-toggle-button'), @@ -64,7 +66,7 @@ test.describe('Scope tests', () => { ).toBeVisible(); await headerNavLocator.click(); - const orgAHeaderNavLink = authedPage.getByRole('link', { + const orgAHeaderNavLink = authedPage.getByRole('option', { name: orgA.name, }); await orgAHeaderNavLink.click(); @@ -79,7 +81,7 @@ test.describe('Scope tests', () => { authedPage.getByRole('link', { name: targetB.name }), ).toBeHidden(); - const orgBHeaderNavLink = authedPage.getByRole('link', { + const orgBHeaderNavLink = authedPage.getByRole('option', { name: orgB.name, }); await expect(orgBHeaderNavLink).toBeVisible(); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1d28b0b79..c605ee565 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -991,6 +991,9 @@ importers: '@xterm/xterm': specifier: ^5.5.0 version: 5.5.0 + ember-a11y-refocus: + specifier: ^5.0.0 + version: 5.1.0(@babel/core@7.27.1)(ember-source@5.12.0(@glimmer/component@2.0.0)(@glint/template@1.5.2)(rsvp@4.8.5)(webpack@5.104.1(esbuild@0.25.9))) ember-inflector: specifier: ^6.0.0 version: 6.0.0(@babel/core@7.27.1) diff --git a/ui/admin/app/components/header-nav/index.hbs b/ui/admin/app/components/header-nav/index.hbs deleted file mode 100644 index 8c5a5b2ad..000000000 --- a/ui/admin/app/components/header-nav/index.hbs +++ /dev/null @@ -1,41 +0,0 @@ -{{! - Copyright IBM Corp. 2021, 2026 - SPDX-License-Identifier: BUSL-1.1 -}} - - - - {{#if this.session.data.authenticated.isGlobal}} - - {{t 'titles.global'}} - - {{/if}} - {{#each this.scope.orgsList as |org|}} - - {{org.displayName}} - - {{/each}} - - -{{#if this.scope.project}} - - - {{#each this.scope.projectsList as |project|}} - - {{project.displayName}} - - {{/each}} - -{{/if}} \ No newline at end of file diff --git a/ui/admin/app/components/header-nav/index.js b/ui/admin/app/components/header-nav/index.js deleted file mode 100644 index abc0830ba..000000000 --- a/ui/admin/app/components/header-nav/index.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright IBM Corp. 2021, 2026 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import Component from '@glimmer/component'; -import { service } from '@ember/service'; - -export default class HeaderNavComponent extends Component { - // =services - - @service session; - @service scope; -} diff --git a/ui/admin/app/components/scope-picker/index.hbs b/ui/admin/app/components/scope-picker/index.hbs index caccb276c..e48c5e931 100644 --- a/ui/admin/app/components/scope-picker/index.hbs +++ b/ui/admin/app/components/scope-picker/index.hbs @@ -4,18 +4,18 @@ }} {{#let this.currentScope as |scope|}} {{/let}} diff --git a/ui/admin/app/components/scope-picker/index.js b/ui/admin/app/components/scope-picker/index.js index 1dac6c6fb..efea299d4 100644 --- a/ui/admin/app/components/scope-picker/index.js +++ b/ui/admin/app/components/scope-picker/index.js @@ -7,7 +7,7 @@ import Component from '@glimmer/component'; import { inject as service } from '@ember/service'; import orderBy from 'lodash/orderBy'; -export default class ScopePickerIndexComponent extends Component { +export default class ScopePickerComponent extends Component { // =services @service intl; diff --git a/ui/admin/app/controllers/application.js b/ui/admin/app/controllers/application.js index 2ecf34299..8bfd7b8b6 100644 --- a/ui/admin/app/controllers/application.js +++ b/ui/admin/app/controllers/application.js @@ -9,7 +9,7 @@ import { getOwner } from '@ember/application'; import { action } from '@ember/object'; import { loading } from 'ember-loading'; import { defaultValidator } from 'ember-a11y-refocus'; -import { paramValueFinder } from 'admin/utils/param-value-finder'; +import { paramValueFinder } from 'core/utils/param-value-finder'; const THEMES = [ { diff --git a/ui/admin/app/routes/scopes/scope/auth-methods/auth-method.js b/ui/admin/app/routes/scopes/scope/auth-methods/auth-method.js index 0b3d3787e..e9bb75eb3 100644 --- a/ui/admin/app/routes/scopes/scope/auth-methods/auth-method.js +++ b/ui/admin/app/routes/scopes/scope/auth-methods/auth-method.js @@ -5,7 +5,7 @@ import Route from '@ember/routing/route'; import { service } from '@ember/service'; -import { paramValueFinder } from 'admin/utils/param-value-finder'; +import { paramValueFinder } from 'core/utils/param-value-finder'; export default class ScopesScopeAuthMethodsAuthMethodRoute extends Route { // =services diff --git a/ui/admin/app/routes/scopes/scope/credential-stores/credential-store.js b/ui/admin/app/routes/scopes/scope/credential-stores/credential-store.js index 754e428a1..68dac83ee 100644 --- a/ui/admin/app/routes/scopes/scope/credential-stores/credential-store.js +++ b/ui/admin/app/routes/scopes/scope/credential-stores/credential-store.js @@ -5,7 +5,7 @@ import Route from '@ember/routing/route'; import { service } from '@ember/service'; -import { paramValueFinder } from 'admin/utils/param-value-finder'; +import { paramValueFinder } from 'core/utils/param-value-finder'; export default class ScopesScopeCredentialStoresCredentialStoreRoute extends Route { // =services diff --git a/ui/admin/app/routes/scopes/scope/host-catalogs/host-catalog.js b/ui/admin/app/routes/scopes/scope/host-catalogs/host-catalog.js index 0baebaad3..0b4ad57b4 100644 --- a/ui/admin/app/routes/scopes/scope/host-catalogs/host-catalog.js +++ b/ui/admin/app/routes/scopes/scope/host-catalogs/host-catalog.js @@ -5,7 +5,7 @@ import Route from '@ember/routing/route'; import { service } from '@ember/service'; -import { paramValueFinder } from 'admin/utils/param-value-finder'; +import { paramValueFinder } from 'core/utils/param-value-finder'; export default class ScopesScopeHostCatalogsHostCatalogRoute extends Route { // =services diff --git a/ui/admin/app/routes/scopes/scope/host-catalogs/host-catalog/host-sets/host-set.js b/ui/admin/app/routes/scopes/scope/host-catalogs/host-catalog/host-sets/host-set.js index 0017911b6..f6ec8a56c 100644 --- a/ui/admin/app/routes/scopes/scope/host-catalogs/host-catalog/host-sets/host-set.js +++ b/ui/admin/app/routes/scopes/scope/host-catalogs/host-catalog/host-sets/host-set.js @@ -5,7 +5,7 @@ import Route from '@ember/routing/route'; import { service } from '@ember/service'; -import { paramValueFinder } from 'admin/utils/param-value-finder'; +import { paramValueFinder } from 'core/utils/param-value-finder'; export default class ScopesScopeHostCatalogsHostCatalogHostSetsHostSetRoute extends Route { // =services diff --git a/ui/admin/app/routes/scopes/scope/session-recordings/session-recording.js b/ui/admin/app/routes/scopes/scope/session-recordings/session-recording.js index 9473e6e23..ee6d54f2c 100644 --- a/ui/admin/app/routes/scopes/scope/session-recordings/session-recording.js +++ b/ui/admin/app/routes/scopes/scope/session-recordings/session-recording.js @@ -5,7 +5,7 @@ import Route from '@ember/routing/route'; import { service } from '@ember/service'; -import { paramValueFinder } from 'admin/utils/param-value-finder'; +import { paramValueFinder } from 'core/utils/param-value-finder'; export default class ScopesScopeSessionRecordingsSessionRecordingRoute extends Route { // =services diff --git a/ui/admin/app/styles/app.scss b/ui/admin/app/styles/app.scss index 7d18ea5e2..8f0e3c7c9 100644 --- a/ui/admin/app/styles/app.scss +++ b/ui/admin/app/styles/app.scss @@ -11,12 +11,6 @@ @use 'notify'; @use 'asciinema-player/dist/bundle/asciinema-player.css'; -.rose-layout-global { - .rose-layout-global-body { - justify-content: center; - } -} - .rose-layout-centered { > .rose-layout-page { width: sizing.rems(l) * 18; @@ -89,14 +83,6 @@ } } -[class*='session-status-'] { - color: var(--ui-gray-subtler-2); -} - -.session-status-active { - color: var(--success); -} - .nowrap { white-space: nowrap; } @@ -1005,18 +991,6 @@ } } -// scope picker -.scope-picker { - &__toggle-button { - width: 100%; - } - - li:has(.indentation), - li.indentation { - margin-left: 1.5rem; - } -} - // dropdown checkbox list .dropdown-checkbox-list { margin-bottom: 1rem; @@ -1027,6 +1001,14 @@ margin-bottom: 1rem; } +// Scope picker in global header +.scope-picker { + li:has(.indentation), + li.indentation { + margin-left: 1.5rem; + } +} + // User menu dropdown in global side nav .user-menu-dropdown { max-width: 125px; diff --git a/ui/admin/app/templates/application.hbs b/ui/admin/app/templates/application.hbs index d1949f3c0..460fed495 100644 --- a/ui/admin/app/templates/application.hbs +++ b/ui/admin/app/templates/application.hbs @@ -5,151 +5,160 @@ {{page-title (app-name)}} - - - + + - <:header> - - <:logo> - - - <:actions> - {{#if (feature-flag 'dev-edition-toggle')}} - - + <:logo> + + - - {{#each this.featureEdition.editions as |edition|}} - - {{edition}} - - {{/each}} - - - - {{#each this.featureEdition.licensedFeatures as |feature|}} - - {{feature}} - - - {{/each}} - - - - {{t 'actions.download-db'}} - - - {{t 'actions.clear-db'}} - - - {{/if}} - - {{#if this.session.username}} - - - - - - - {{t - 'actions.change-password' - }} - - - {{t - 'actions.deauthenticate' - }} - - - - {{#each this.themes as |theme|}} - - {{t (concat 'themes.' theme.label)}} - - {{/each}} - - {{else}} - - - {{t - 'actions.change-password' - }} - - - {{t - 'actions.deauthenticate' - }} - - - - {{#each this.themes as |theme|}} - - {{t (concat 'themes.' theme.label)}} - - {{/each}} - - {{/if}} - - - - - <:body> - - - - <:footer> + <:globalActions> - - + + + <:utilityActions> + {{#if (feature-flag 'dev-edition-toggle')}} + + + + {{#each this.featureEdition.editions as |edition|}} + + {{edition}} + + {{/each}} + + + + {{#each this.featureEdition.licensedFeatures as |feature|}} + + {{feature}} + + {{/each}} + + + + + {{t 'actions.download-db'}} + + + {{t 'actions.clear-db'}} + + + {{/if}} + + {{#if this.session.username}} + + + + + + + {{t + 'actions.change-password' + }} + + + {{t + 'actions.deauthenticate' + }} + + + + {{#each this.themes as |theme|}} + + {{t (concat 'themes.' theme.label)}} + + {{/each}} + + {{else}} + + + {{t + 'actions.change-password' + }} + + + {{t + 'actions.deauthenticate' + }} + + + + {{#each this.themes as |theme|}} + + {{t (concat 'themes.' theme.label)}} + + {{/each}} + + {{/if}} + + + + + + + + + diff --git a/ui/admin/app/templates/authentication-complete.hbs b/ui/admin/app/templates/authentication-complete.hbs index 100c42996..34a4f95f1 100644 --- a/ui/admin/app/templates/authentication-complete.hbs +++ b/ui/admin/app/templates/authentication-complete.hbs @@ -5,11 +5,9 @@ {{page-title (t 'auth.titles.flow-success')}} -
- - - {{t 'auth.titles.flow-success'}} - {{t 'auth.descriptions.flow-success'}} - - -
\ No newline at end of file + + + {{t 'auth.titles.flow-success'}} + {{t 'auth.descriptions.flow-success'}} + + \ No newline at end of file diff --git a/ui/admin/app/templates/authentication-error.hbs b/ui/admin/app/templates/authentication-error.hbs index 1d511f893..993c554ac 100644 --- a/ui/admin/app/templates/authentication-error.hbs +++ b/ui/admin/app/templates/authentication-error.hbs @@ -5,14 +5,12 @@ {{page-title (t 'auth.titles.flow-error')}} -
- - - {{t 'auth.titles.flow-error'}} - - {{t 'auth.descriptions.flow-error'}} - - - - -
\ No newline at end of file + + + {{t 'auth.titles.flow-error'}} + + {{t 'auth.descriptions.flow-error'}} + + + + \ No newline at end of file diff --git a/ui/admin/app/templates/scopes/scope.hbs b/ui/admin/app/templates/scopes/scope.hbs index 66ca48aad..feb71b60a 100644 --- a/ui/admin/app/templates/scopes/scope.hbs +++ b/ui/admin/app/templates/scopes/scope.hbs @@ -33,7 +33,11 @@ /> {{/if}} - + {{#if @model.isGlobal}} {{t 'titles.scope-level' scope=(t 'titles.global')}} @@ -243,6 +247,5 @@ @route='scopes.scope.edit' /> {{/if}} - - + {{outlet}} \ No newline at end of file diff --git a/ui/admin/tests/acceptance/accounts/change-password-test.js b/ui/admin/tests/acceptance/accounts/change-password-test.js index b6ad21f44..eea4b29f6 100644 --- a/ui/admin/tests/acceptance/accounts/change-password-test.js +++ b/ui/admin/tests/acceptance/accounts/change-password-test.js @@ -57,7 +57,7 @@ module('Acceptance | accounts | change password', function (hooks) { await visit(urls.orgScope); - await click(commonSelectors.SIDEBAR_USER_DROPDOWN); + await click(commonSelectors.HEADER_USER_DROPDOWN); await click(commonSelectors.HREF(urls.changePassword)); assert.strictEqual(currentURL(), urls.changePassword); @@ -150,7 +150,7 @@ module('Acceptance | accounts | change password', function (hooks) { }); await visit(urls.orgScope); - await click(commonSelectors.SIDEBAR_USER_DROPDOWN); + await click(commonSelectors.HEADER_USER_DROPDOWN); await click(commonSelectors.HREF(urls.changePassword)); await fillIn( selectors.FIELD_CURRENT_PASSWORD, diff --git a/ui/admin/tests/acceptance/authentication-test.js b/ui/admin/tests/acceptance/authentication-test.js index dada02d88..5f7f0261a 100644 --- a/ui/admin/tests/acceptance/authentication-test.js +++ b/ui/admin/tests/acceptance/authentication-test.js @@ -379,7 +379,7 @@ module('Acceptance | authentication', function (hooks) { assert.notOk(getRootElement().classList.contains('rose-theme-dark')); // open dropdown - await click(commonSelectors.SIDEBAR_USER_DROPDOWN); + await click(commonSelectors.HEADER_USER_DROPDOWN); // toggle system default await click(commonSelectors.TOGGLE_THEME_DEFAULT); diff --git a/ui/admin/tests/acceptance/scopes-test.js b/ui/admin/tests/acceptance/scopes-test.js index 754baeaf8..af8108761 100644 --- a/ui/admin/tests/acceptance/scopes-test.js +++ b/ui/admin/tests/acceptance/scopes-test.js @@ -133,12 +133,12 @@ module('Acceptance | scopes', function (hooks) { assert.strictEqual(currentURL(), urls.globalScope); - await click(commonSelectors.SIDEBAR_SCOPE_DROPDOWN); - await click(commonSelectors.SIDEBAR_SCOPE_LINK(urls.orgScope)); + await click(commonSelectors.HEADER_SCOPE_DROPDOWN); + await click(commonSelectors.HEADER_SCOPE_LINK(urls.orgScope)); assert.strictEqual(currentURL(), urls.orgScopes); - await click(commonSelectors.SIDEBAR_SCOPE_DROPDOWN); - await click(commonSelectors.SIDEBAR_SCOPE_LINK(urls.org2Scope)); + await click(commonSelectors.HEADER_SCOPE_DROPDOWN); + await click(commonSelectors.HEADER_SCOPE_LINK(urls.org2Scope)); assert.strictEqual(currentURL(), urls.org2Scopes); }); @@ -167,8 +167,8 @@ module('Acceptance | scopes', function (hooks) { assert.strictEqual(currentURL(), urls.projectTargets); - await click(commonSelectors.SIDEBAR_SCOPE_DROPDOWN); - await click(commonSelectors.SIDEBAR_SCOPE_LINK(urls.project2Scope)); + await click(commonSelectors.HEADER_SCOPE_DROPDOWN); + await click(commonSelectors.HEADER_SCOPE_LINK(urls.project2Scope)); assert.strictEqual(currentURL(), urls.project2Targets); }); diff --git a/ui/admin/tests/helpers/selectors.js b/ui/admin/tests/helpers/selectors.js index 80865bdc7..98406f335 100644 --- a/ui/admin/tests/helpers/selectors.js +++ b/ui/admin/tests/helpers/selectors.js @@ -44,21 +44,18 @@ export const PAGE_MESSAGE_LINK = export const HREF = (url) => `[href="${url}"]`; export const SIDEBAR_NAV_LINK = (url) => - `.hds-side-nav__content a[href="${url}"]`; -export const SIDEBAR_NAV_CONTENT = '.hds-side-nav__content'; -export const SIDEBAR_USER_DROPDOWN = - '[data-test-side-nav-user-dropdown] button'; -export const SIDEBAR_SCOPE_DROPDOWN = - '[data-test-side-nav-scope-dropdown] button'; -export const SIDEBAR_SCOPE_LINK = (url) => - `[data-test-side-nav-scope-dropdown] a[href="${url}"]`; + `.hds-app-side-nav__list a[href="${url}"]`; +export const SIDEBAR_NAV_CONTENT = '.hds-app-side-nav__list'; +export const HEADER_USER_DROPDOWN = '[data-test-header-user-dropdown] button'; +export const HEADER_SCOPE_DROPDOWN = '[data-test-header-scope-dropdown] button'; +export const HEADER_SCOPE_LINK = (url) => + `[data-test-header-scope-dropdown] a[href="${url}"]`; export const TOGGLE_THEME_DEFAULT = - '[data-test-side-nav-user-dropdown] [value=system-default-theme]'; + '[data-test-header-user-dropdown] [value=system-default-theme]'; export const TOGGLE_THEME_LIGHT = - '[data-test-side-nav-user-dropdown] [value=light]'; + '[data-test-header-user-dropdown] [value=light]'; export const TOGGLE_THEME_DARK = - '[data-test-side-nav-user-dropdown] [value=dark]'; - + '[data-test-header-user-dropdown] [value=dark]'; export const ALERT_TOAST = '[data-test-toast-notification]'; export const ALERT_TOAST_BODY = '[data-test-toast-notification] .hds-alert__description'; diff --git a/ui/admin/tests/integration/components/scope-picker/index-test.js b/ui/admin/tests/integration/components/scope-picker/index-test.js index 50648cf4e..d50434e66 100644 --- a/ui/admin/tests/integration/components/scope-picker/index-test.js +++ b/ui/admin/tests/integration/components/scope-picker/index-test.js @@ -4,7 +4,7 @@ */ import { module, test } from 'qunit'; -import { setupRenderingTest } from 'admin/tests/helpers'; +import { setupRenderingTest } from 'ember-qunit'; import { render } from '@ember/test-helpers'; import { hbs } from 'ember-cli-htmlbars'; import { click } from '@ember/test-helpers'; @@ -12,7 +12,7 @@ import { setupMirage } from 'admin/tests/helpers/mirage'; import { setupIntl } from 'ember-intl/test-support'; import { setupSqlite } from 'api/test-support/helpers/sqlite'; -module('Integration | Component | scope-picker/index', function (hooks) { +module('Integration | Component | scope-picker', function (hooks) { setupRenderingTest(hooks); setupMirage(hooks); setupIntl(hooks, 'en-us'); @@ -27,7 +27,8 @@ module('Integration | Component | scope-picker/index', function (hooks) { const PROJECT_ADD_MORE_TEXT = '[data-test-scope-picker-project-and-more-text]'; const PROJECT_LIST_COUNT = '[data-test-scope-picker-project-count]'; - const SELECTED_SCOPE_CHECK = (id) => `[href="/scopes/${id}"] .hds-icon-check`; + const SELECTED_SCOPE_CHECK = + '.hds-dropdown-list-item--variant-checkmark-selected .hds-text'; let global; let scopeService; @@ -195,7 +196,7 @@ module('Integration | Component | scope-picker/index', function (hooks) { assert.dom(SCOPE_PICKER_DROPDOWN).hasText('Global'); assert.dom(SCOPE_PICKER_DROPDOWN_ICON).hasClass('hds-icon-globe'); - assert.dom(SELECTED_SCOPE_CHECK('global')).isVisible(); + assert.dom(SELECTED_SCOPE_CHECK).hasText('Global'); }); test('it renders correct dropdown button text and icon when selected scope is org type', async function (assert) { @@ -215,7 +216,7 @@ module('Integration | Component | scope-picker/index', function (hooks) { assert.dom(SCOPE_PICKER_DROPDOWN).hasText(scopeService.org.displayName); assert.dom(SCOPE_PICKER_DROPDOWN_ICON).hasClass('hds-icon-org'); - assert.dom(SELECTED_SCOPE_CHECK(scopeService.org.id)).isVisible(); + assert.dom(SELECTED_SCOPE_CHECK).hasText(scopeService.org.displayName); }); test('it renders correct dropdown button text and icon when selected scope is project type', async function (assert) { @@ -229,7 +230,6 @@ module('Integration | Component | scope-picker/index', function (hooks) { }); query.filters.scope_id = [{ equals: org.id }]; const projects = await store.query('scope', { scope_id: org.id, query }); - scopeService.org = global; scopeService.project = projects[0]; query.filters.scope_id = [{ equals: 'global' }]; scopeService.orgsList = await store.query('scope', { @@ -243,6 +243,6 @@ module('Integration | Component | scope-picker/index', function (hooks) { assert.dom(SCOPE_PICKER_DROPDOWN).hasText(scopeService.project.displayName); assert.dom(SCOPE_PICKER_DROPDOWN_ICON).hasClass('hds-icon-grid'); - assert.dom(SELECTED_SCOPE_CHECK(scopeService.project.id)).isVisible(); + assert.dom(SELECTED_SCOPE_CHECK).hasText(scopeService.project.displayName); }); }); diff --git a/ui/admin/tests/unit/utils/param-value-finder-test.js b/ui/admin/tests/unit/utils/param-value-finder-test.js deleted file mode 100644 index f20e38998..000000000 --- a/ui/admin/tests/unit/utils/param-value-finder-test.js +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright IBM Corp. 2021, 2026 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; -import { paramValueFinder } from 'admin/utils/param-value-finder'; - -module('Unit | Utility | param-value-finder', function (hooks) { - setupTest(hooks); - - let router; - - hooks.beforeEach(function () { - router = this.owner.lookup('service:router'); - }); - - test('returns empty array when null or empty routeInfo object is used', function (assert) { - let result = paramValueFinder('scopes', null); - - assert.deepEqual(result, []); - - result = paramValueFinder('scopes', {}); - - assert.deepEqual(result, []); - }); - - test('returns correct array when routeInfo object is used', function (assert) { - let routeInfo = router.recognize('/scopes'); - let result = paramValueFinder('scopes', routeInfo.parent); - - assert.deepEqual(result, []); - - routeInfo = router.recognize('/scopes/global/auth-methods'); - result = paramValueFinder('scopes', routeInfo.parent); - assert.deepEqual(result, ['global']); - }); -}); diff --git a/ui/desktop/app/controllers/application.js b/ui/desktop/app/controllers/application.js index b2464c96d..c9d64b536 100644 --- a/ui/desktop/app/controllers/application.js +++ b/ui/desktop/app/controllers/application.js @@ -8,6 +8,8 @@ import { service } from '@ember/service'; import { getOwner } from '@ember/application'; import { action } from '@ember/object'; import { tracked } from '@glimmer/tracking'; +import { defaultValidator } from 'ember-a11y-refocus'; +import { paramValueFinder } from 'core/utils/param-value-finder'; export default class ApplicationController extends Controller { // =services @@ -17,6 +19,9 @@ export default class ApplicationController extends Controller { @service ipc; @service session; @service('browser/window') window; + @service router; + @service scope; + @service intl; // =attributes @@ -34,6 +39,30 @@ export default class ApplicationController extends Controller { }); } + /** + * Returns display name and icon for current scope. + * @type {object} + */ + get currentScope() { + if (this.scope.org.isOrg) { + return { name: this.scope.org.displayName, icon: 'org' }; + } else { + return { name: this.intl.t('titles.global'), icon: 'globe' }; + } + } + + /** + * Shows side navigation only for routes nested under a scope + * and if user has been authenticated. + * @type {boolean} + */ + get showSideNav() { + return ( + this.router.currentRouteName.startsWith('scopes.scope') && + this.session.isAuthenticated + ); + } + // =actions /** @@ -128,4 +157,29 @@ export default class ApplicationController extends Controller { super.willDestroy(...arguments); this.removeOnAppQuitListener?.(); } + + /** + * Add custom route change validation to prevent refocus when + * user is attempting to search, filter, or sort. + * @param {object} transition + * @returns {boolean} + */ + customRouteChangeValidator(transition) { + if (!transition.to || !transition.from) { + return true; + } + if (transition.to.name === transition.from.name) { + const toParams = paramValueFinder( + transition.to.localName, + transition.to.parent, + ); + const fromParams = paramValueFinder( + transition.from.localName, + transition.from.parent, + ); + // Return false to prevent refocus when routes have equivalent dynamic segments (params). + return JSON.stringify(toParams) !== JSON.stringify(fromParams); + } + return defaultValidator(transition); + } } diff --git a/ui/desktop/app/controllers/scopes/scope.js b/ui/desktop/app/controllers/scopes/scope.js deleted file mode 100644 index 21add14bc..000000000 --- a/ui/desktop/app/controllers/scopes/scope.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright IBM Corp. 2021, 2026 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import Controller from '@ember/controller'; -import { service } from '@ember/service'; - -export default class ScopesScopeController extends Controller { - // =services - - @service session; -} diff --git a/ui/desktop/app/routes/scopes/scope.js b/ui/desktop/app/routes/scopes/scope.js index c9d591417..8bac557d6 100644 --- a/ui/desktop/app/routes/scopes/scope.js +++ b/ui/desktop/app/routes/scopes/scope.js @@ -53,12 +53,12 @@ export default class ScopesScopeRoute extends Route { */ async afterModel(model) { // First, load orgs and, if necessary, projects - let orgs, projects; + let orgs; orgs = await this.store .query('scope', { scope_id: 'global' }) .catch(() => new TrackedArray([])); if (model.isProject) { - projects = await this.store.query('scope', { scope_id: model.scopeID }); + await this.store.query('scope', { scope_id: model.scopeID }); } // Then pull out the "selected" scopes, if relevant let selectedOrg, selectedProject; @@ -70,16 +70,6 @@ export default class ScopesScopeRoute extends Route { // Update the scope service with the current scope(s); this.scope.org = selectedOrg; this.scope.project = selectedProject; - this.scopes = { orgs, projects, selectedOrg, selectedProject }; - } - - /** - * Adds the scopes hash to the controller context (see `afterModel`). - * @param {Controller} controller - */ - setupController(controller) { - super.setupController(...arguments); - const scopes = this.scopes; - controller.setProperties({ scopes }); + this.scope.orgsList = orgs; } } diff --git a/ui/desktop/app/styles/app.scss b/ui/desktop/app/styles/app.scss index 72da3d052..f2664d71b 100644 --- a/ui/desktop/app/styles/app.scss +++ b/ui/desktop/app/styles/app.scss @@ -24,34 +24,6 @@ } } -/** - * This section enables scrolling within the sidebar columns separately, - * rather than the whole viewport. This ensures the sidebar and application - * header remain visible when scrolling. - */ -.rose-layout-global { - max-height: 100vh; - - .rose-layout-global-body { - max-height: 100vh; - overflow: auto; - - main { - /** This overrides the main defined within Rose. - * HDS has this set to 1.5rem top/bottom 3rem left/right. HDS assumes the usage of breadcrumbs - * but the DC have no breadcrumbs, so we will keep it at 3rem all around. - */ - padding: 3rem; - flex-grow: 1; - } - } - - .rose-layout-sidebar-sidebar, - .rose-layout-sidebar-body { - overflow-y: auto; - } -} - .rose-nav-sidebar { margin-top: sizing.rems(xxl); @@ -74,95 +46,11 @@ } } -.rose-header { - -webkit-app-region: drag; - min-height: sizing.rems(l) * 2; - position: relative; - -webkit-user-select: none; - - .rose-header-brand, - .rose-header-nav, - .rose-header-utilities button { - -webkit-app-region: no-drag; - } - - &.header-cushion { - padding-left: sizing.rems(l) * 3; - } - - .button-window-minimize, - .button-window-fullscreen, - .button-window-close { - background: transparent; - border: transparent; - color: var(--white); - margin: sizing.rems(xs); - } - - .button-window-close { - &:hover { - background: var(--failure); - } - } - - .button-window-minimize, - .button-window-fullscreen { - &:hover { - background: var(--white); - color: var(--black); - } - } - - .logotype { - display: none; - } - - &.loading { - &::after { - animation: gradient 1s linear infinite; - background: repeating-linear-gradient( - to right, - var(--subtle), - var(--subtle), - var(--info), - var(--subtle), - var(--subtle) - ); - background-position: -100% 0; - background-size: 200% auto; - content: ' '; - height: 3px; - left: 0; - position: absolute; - right: 0; - top: 100%; - - @keyframes gradient { - 0% { - background-position: -100% 0; - } - - 100% { - background-position: -200% 0; - } - } - } - } -} - -[class*='session-status-'] { - color: var(--ui-gray-subtler-2); -} - -.nowrap { - white-space: nowrap; -} - .branded-card { border: 1px solid var(--ui-border-subtler-1); border-radius: var(--token-border-radius-x-small); margin: 0 auto; - margin-top: sizing.rems(l) * 1.25; + margin-top: sizing.rems(xl) * 1.5; padding: (sizing.rems(l) * 4) (sizing.rems(l) * 3) (sizing.rems(l) * 2) (sizing.rems(l) * 3); position: relative; @@ -424,3 +312,44 @@ } } } + +.app-header { + -webkit-app-region: drag; + user-select: none; + + &.header-cushion { + padding-left: sizing.rems(l) * 4; + } + + &__logo, + &__global-actions, + &__utility-actions, + .hds-app-header__menu-button { + -webkit-app-region: no-drag; + } + + .hds-app-header__utility-actions { + .button-window-minimize, + .button-window-fullscreen, + .button-window-close { + background: transparent; + border: transparent; + color: var(--white); + margin: sizing.rems(xs); + } + + .button-window-close { + &:hover { + background: var(--failure); + } + } + + .button-window-minimize, + .button-window-fullscreen { + &:hover { + background: var(--white); + color: var(--black); + } + } + } +} diff --git a/ui/desktop/app/templates/application.hbs b/ui/desktop/app/templates/application.hbs index 61eda7d87..baac989b3 100644 --- a/ui/desktop/app/templates/application.hbs +++ b/ui/desktop/app/templates/application.hbs @@ -5,55 +5,76 @@ {{page-title (app-name)}} - - - - + + - + <:logo> + - + {{#if this.showWindowActions}} + + + + {{/if}} + - + + + + + + + + + + {{outlet}} - - - +
+
{{#if this.flashMessages.queue}}
diff --git a/ui/desktop/app/templates/cluster-url.hbs b/ui/desktop/app/templates/cluster-url.hbs index 0b3272784..23febe6ec 100644 --- a/ui/desktop/app/templates/cluster-url.hbs +++ b/ui/desktop/app/templates/cluster-url.hbs @@ -3,41 +3,39 @@ SPDX-License-Identifier: BUSL-1.1 }} -
- + - + {{t 'form.cluster-url.label'}} + {{t 'form.cluster-url.description'}} + - {{! template-lint-disable no-autofocus-attribute }} - - {{t 'form.cluster-url.label'}} - {{t 'form.cluster-url.description'}} - - - - <:actions> - - - - - -
\ No newline at end of file + + <:actions> + + + + + \ No newline at end of file diff --git a/ui/desktop/app/templates/error.hbs b/ui/desktop/app/templates/error.hbs index 19bca86bf..78a0299a3 100644 --- a/ui/desktop/app/templates/error.hbs +++ b/ui/desktop/app/templates/error.hbs @@ -5,18 +5,14 @@ {{page-title (t 'errors.generic.title')}} -
+ + + - - - - - - - - -
\ No newline at end of file + + + \ No newline at end of file diff --git a/ui/desktop/app/templates/scopes/scope.hbs b/ui/desktop/app/templates/scopes/scope.hbs index db0423137..2a6441f81 100644 --- a/ui/desktop/app/templates/scopes/scope.hbs +++ b/ui/desktop/app/templates/scopes/scope.hbs @@ -3,31 +3,4 @@ SPDX-License-Identifier: BUSL-1.1 }} -{{outlet}} - -{{#if this.session.isAuthenticated}} - - - - - {{t 'titles.global'}} - - {{#if this.scopes.orgs.length}} - - {{/if}} - {{#each this.scopes.orgs as |scope|}} - - {{scope.displayName}} - - {{/each}} - - -{{/if}} \ No newline at end of file +{{outlet}} \ No newline at end of file diff --git a/ui/desktop/app/templates/scopes/scope/authenticate.hbs b/ui/desktop/app/templates/scopes/scope/authenticate.hbs index a952b057a..e554dbd29 100644 --- a/ui/desktop/app/templates/scopes/scope/authenticate.hbs +++ b/ui/desktop/app/templates/scopes/scope/authenticate.hbs @@ -5,76 +5,72 @@ {{page-title (t 'actions.authenticate')}} -
- {{#if this.isOIDCRoute}} - {{outlet}} - {{else}} - +{{#if this.isOIDCRoute}} + {{outlet}} +{{else}} + - - - - {{t 'titles.global'}} + + + + {{t 'titles.global'}} + + {{#each this.sortedScopes as |scope|}} + + {{scope.displayName}} - {{#each this.sortedScopes as |scope|}} - - {{scope.displayName}} - - {{/each}} - + {{/each}} + - - {{#each @model.authMethods as |authMethod|}} - - {{#if authMethod.name}} - {{authMethod.name}} - {{else}} - {{t (concat 'resources.auth-method.types.' authMethod.type)}} - {{/if}} - - {{/each}} - + + {{#each @model.authMethods as |authMethod|}} + + {{#if authMethod.name}} + {{authMethod.name}} + {{else}} + {{t (concat 'resources.auth-method.types.' authMethod.type)}} + {{/if}} + + {{/each}} + - {{#if @model.authMethods}} - {{outlet}} - {{else}} - - - - - - - {{/if}} - - {{t 'cluster-url.connected-to'}}: - - {{this.clusterUrl}} - -
- - {{t 'actions.change-cluster-url'}} - -
-
- {{/if}} -
\ No newline at end of file + {{#if @model.authMethods}} + {{outlet}} + {{else}} + + + + + + + {{/if}} + + {{t 'cluster-url.connected-to'}}: + + {{this.clusterUrl}} + +
+ + {{t 'actions.change-cluster-url'}} + +
+ +{{/if}} \ No newline at end of file diff --git a/ui/desktop/app/templates/scopes/scope/projects.hbs b/ui/desktop/app/templates/scopes/scope/projects.hbs index fa2d34e6f..2f96db40b 100644 --- a/ui/desktop/app/templates/scopes/scope/projects.hbs +++ b/ui/desktop/app/templates/scopes/scope/projects.hbs @@ -3,28 +3,26 @@ SPDX-License-Identifier: BUSL-1.1 }} - - - - - - {{t 'resources.target.title_plural'}} - + + + + + - - - {{t 'resources.session.title_plural'}} - - - - {{t 'titles.settings'}} - - - - - -
- {{outlet}} -
-
-
\ No newline at end of file +{{outlet}} \ No newline at end of file diff --git a/ui/desktop/package.json b/ui/desktop/package.json index 64eed3d81..d6925bd9c 100644 --- a/ui/desktop/package.json +++ b/ui/desktop/package.json @@ -46,6 +46,7 @@ "@xterm/addon-canvas": "^0.7.0", "@xterm/addon-fit": "^0.10.0", "@xterm/xterm": "^5.5.0", + "ember-a11y-refocus": "^5.0.0", "ember-inflector": "^6.0.0", "ember-named-blocks-polyfill": "^0.2.5", "lodash": "^4.17.23", diff --git a/ui/desktop/tests/acceptance/application-test.js b/ui/desktop/tests/acceptance/application-test.js index 4cb6971b4..ead61cede 100644 --- a/ui/desktop/tests/acceptance/application-test.js +++ b/ui/desktop/tests/acceptance/application-test.js @@ -67,7 +67,7 @@ module('Acceptance | Application', function (hooks) { stubs.ipcService.withArgs('hasMacOSChrome').returns(true); await visit(urls.clusterUrl); assert.ok( - find('.rose-header.header-cushion'), + find('.app-header.header-cushion'), 'Adds header padding around native window actions', ); }); @@ -77,7 +77,7 @@ module('Acceptance | Application', function (hooks) { stubs.ipcService.withArgs('hasMacOSChrome').returns(false); await visit(urls.clusterUrl); assert.notOk( - find('.rose-header.header-cushion'), + find('.app-header.header-cushion'), 'Does not add header padding around native window actions', ); }); diff --git a/ui/desktop/tests/acceptance/authentication-test.js b/ui/desktop/tests/acceptance/authentication-test.js index 4d2fa2d7c..a1ffd0c49 100644 --- a/ui/desktop/tests/acceptance/authentication-test.js +++ b/ui/desktop/tests/acceptance/authentication-test.js @@ -69,8 +69,7 @@ module('Acceptance | authentication', function (hooks) { const MODAL_CLOSE_SESSIONS = '[data-test-close-sessions-modal]'; const MODAL_CONFIRM_BTN = '.hds-modal__footer .hds-button--color-primary'; const MODAL_CANCEL_BTN = '.hds-modal__footer .hds-button--color-secondary'; - const HEADER_DROPDOWN_BTN = - '.rose-header-utilities .header-dropdown-button-override button'; + const HEADER_DROPDOWN_BTN = '.app-header__utility-actions button'; const setDefaultClusterUrl = (test) => { const windowOrigin = window.location.origin; diff --git a/ui/desktop/tests/acceptance/projects/sessions/index-test.js b/ui/desktop/tests/acceptance/projects/sessions/index-test.js index 13055db9b..122a07f9f 100644 --- a/ui/desktop/tests/acceptance/projects/sessions/index-test.js +++ b/ui/desktop/tests/acceptance/projects/sessions/index-test.js @@ -542,8 +542,10 @@ module('Acceptance | projects | sessions | index', function (hooks) { .isVisible(); // change scope in app header - await click('.rose-header-nav .hds-dropdown-toggle-button'); - await click('.rose-header-nav .hds-dropdown-list-item:nth-of-type(4) a'); + await click('[data-test-header-scope-dropdown] button'); + await click( + `[data-test-header-scope-dropdown] a[href="${urls.scopes.org2}"]`, + ); // navigate to back sessions await click(`[href="${urls.sessions2}"]`); diff --git a/ui/desktop/tests/acceptance/projects/targets/index-test.js b/ui/desktop/tests/acceptance/projects/targets/index-test.js index fad86505d..1e5fbbdec 100644 --- a/ui/desktop/tests/acceptance/projects/targets/index-test.js +++ b/ui/desktop/tests/acceptance/projects/targets/index-test.js @@ -602,8 +602,10 @@ module('Acceptance | projects | targets | index', function (hooks) { .dom(`[data-test-target-project-id="${instances.scopes.project2.id}"]`) .exists(); - await click('.rose-header-nav .hds-dropdown-toggle-button'); - await click('.rose-header-nav .hds-dropdown-list-item:nth-of-type(3) a'); + await click('[data-test-header-scope-dropdown] button'); + await click( + `[data-test-header-scope-dropdown] a[href="${urls.scopes.org}"]`, + ); assert .dom(`[data-test-target-project-id="${instances.scopes.project.id}"]`) diff --git a/ui/desktop/tests/acceptance/scopes-test.js b/ui/desktop/tests/acceptance/scopes-test.js index 2c92608b8..406cc008d 100644 --- a/ui/desktop/tests/acceptance/scopes-test.js +++ b/ui/desktop/tests/acceptance/scopes-test.js @@ -258,17 +258,23 @@ module('Acceptance | scopes', function (hooks) { ); await visit(urls.targets); - await click('.rose-header-nav .hds-dropdown-toggle-button'); - - await click('.rose-header-nav .hds-dropdown-list-item:nth-of-type(3) a'); + await click('[data-test-header-scope-dropdown] button'); + await click( + `[data-test-header-scope-dropdown] a[href="${urls.scopes.org}"]`, + ); assert.strictEqual(currentURL(), urls.targets); - await click('.rose-header-nav .hds-dropdown-list-item:nth-of-type(4) a'); + await click( + `[data-test-header-scope-dropdown] a[href="${urls.scopes.org2}"]`, + ); assert.strictEqual(currentURL(), urls.org2Targets); - await click('.rose-header-nav .hds-dropdown-list-item:nth-of-type(1) a'); + await click('[data-test-header-scope-dropdown] button'); + await click( + `[data-test-header-scope-dropdown] a[href="${urls.scopes.global}"]`, + ); assert.strictEqual(currentURL(), urls.globalTargets); });