Refactor hds side nav to hds app side nav and header (#3032)

* refactor: 💡 use hds app header and app side nav

* style: 💄 fix dark-mode colors for side nav and app header

* style: 💄 cleanup

* style: 💄 simplify selector

* refactor: 💡 move util (param finder) to core addon

* refactor: 💡 move scope picker to core addon

* refactor: 💡 use hds header and app side nav in ui/desktop

* refactor: 💡 clean up ui/desktop files

* refactor: 💡 remove uneeded header components

* refactor: 💡 remove uneeded global layout

* Add missing copyright headers

* style: 💄 adjust foreground and background colors to fix contras

* style: 💄 remove uneeded stylelint disables

* style: 💄 fix dark mode styling in desktop ui

* refactor: 💡 readd user-select style

* test: 💍 fix tests + e2e tests for admin and desktop UIs

* refactor: 💡 clean up uneeded main elements and fix color link

* Add missing copyright headers

* fix: 🐛 e2e test selectors for ui/desktop

* style: 💄 revert link color change

* refactor: 💡 remove hds override

* fix: 🐛 scope dropdown for desktop

* fix: 🐛 max-height to desktop scope dropdown

* Add missing copyright headers

* chore: 🤖 update lockfile due to webpack update
This commit is contained in:
Lisbet Alvarez authored and GitHub committed 2026-02-25 13:55:36 -08:00
1 parent aaabfbe54d
commit b1b49edb39
94 files changed
+784 -1178

No files matched your search

+54
View File
@@ -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);
}
}
@@ -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;
}
+3 -13
View File
@@ -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;
}
}
+42 -113
View File
@@ -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);
}
}
}
}
+109 -50
View File
@@ -5,55 +5,76 @@
{{page-title (app-name)}}
<Rose::Layout::Global as |layout|>
<layout.header>
<Rose::Header
class='{{if (is-loading) " loading"}}
{{if this.hasMacOSChrome " header-cushion"}}'
as |header|
<Hds::AppFrame @hasHeader={{true}} @hasSidebar={{this.showSideNav}} as |Frame|>
<Frame.Header>
<Hds::AppHeader
class='app-header
{{if this.hasMacOSChrome " header-cushion"}}
app-header-dark-mode-overrides'
@a11yRefocusRouteChangeValidator={{this.customRouteChangeValidator}}
>
<header.brand @logo='logo-app' @text={{(app-name)}} />
<:logo>
<Hds::AppHeader::HomeLink
@icon='boundary'
@color='var(--token-color-boundary-brand)'
@text={{(app-name)}}
@route='index'
class='app-header__logo home-link-color-override'
/>
</:logo>
<header.nav>
<div id='header-nav'></div>
</header.nav>
<:globalActions>
{{#if this.session.isAuthenticated}}
<Hds::Dropdown
class='app-header__global-actions'
@enableCollisionDetection={{true}}
@listPosition='bottom-left'
@height='20rem'
data-test-header-scope-dropdown
as |D|
>
{{#let this.currentScope as |scope|}}
<D.ToggleButton
@text={{scope.name}}
@icon={{scope.icon}}
class='dropdown-toggle-color-override'
/>
{{/let}}
<header.utilities>
{{#if this.showWindowActions}}
<Hds::Button
{{on 'click' this.close}}
class='button-window-close'
title={{t 'actions.close'}}
@text={{t 'actions.close'}}
@icon='x'
@isIconOnly={{true}}
/>
<Hds::Button
{{on 'click' this.toggleFullScreen}}
class='button-window-fullscreen'
title={{t 'actions.fullscreen'}}
@text={{t 'actions.fullscreen'}}
@icon='square'
@isIconOnly={{true}}
@color='secondary'
/>
<Hds::Button
{{on 'click' this.minimize}}
class='button-window-minimize'
title={{t 'actions.minimize'}}
@text={{t 'actions.minimize'}}
@icon='minus'
@isIconOnly={{true}}
@color='secondary'
/>
<D.Checkmark
@icon='globe'
@route='scopes.scope'
@model='global'
@selected={{this.scope.org.isGlobal}}
>
{{t 'titles.global'}}
</D.Checkmark>
{{#if this.scope.orgsList}}
<D.Separator />
{{/if}}
{{#each this.scope.orgsList as |org|}}
<D.Checkmark
@icon='org'
@route='scopes.scope'
@model={{org.id}}
@selected={{eq this.scope.org.id org.id}}
>
{{org.displayName}}
</D.Checkmark>
{{/each}}
</Hds::Dropdown>
{{/if}}
</:globalActions>
<:utilityActions>
{{#if this.session.username}}
<Hds::Dropdown class='header-dropdown-button-override' as |dd|>
<Hds::Dropdown class='app-header__utility-actions' as |dd|>
<dd.ToggleButton
@icon='user-circle'
@text={{this.session.username}}
class='dropdown-toggle-color-override'
/>
<dd.Interactive
data-test-nav-signout-btn
@@ -62,9 +83,13 @@
{{t 'actions.deauthenticate'}}
</dd.Interactive>
</Hds::Dropdown>
{{else}}
<Hds::Dropdown class='header-dropdown-button-override' as |dd|>
<dd.ToggleIcon @icon='user-circle' @text={{t 'titles.user-menu'}} />
{{else if this.session.isAuthenticated}}
<Hds::Dropdown as |dd|>
<dd.ToggleIcon
@icon='user-circle'
@text={{t 'titles.user-menu'}}
class='dropdown-toggle-color-override'
/>
<dd.Interactive
data-test-nav-signout-btn
{{on 'click' this.showModalOrLogout}}
@@ -74,15 +99,49 @@
</Hds::Dropdown>
{{/if}}
</header.utilities>
</Rose::Header>
</layout.header>
{{#if this.showWindowActions}}
<Hds::Button
{{on 'click' this.minimize}}
class='app-header__utility-actions button-window-minimize'
title={{t 'actions.minimize'}}
@text={{t 'actions.minimize'}}
@icon='minus'
@isIconOnly={{true}}
@color='secondary'
/>
<Hds::Button
{{on 'click' this.toggleFullScreen}}
class='app-header__utility-actions button-window-fullscreen'
title={{t 'actions.fullscreen'}}
@text={{t 'actions.fullscreen'}}
@icon='square'
@isIconOnly={{true}}
@color='secondary'
/>
<Hds::Button
{{on 'click' this.close}}
class='app-header__utility-actions button-window-close'
title={{t 'actions.close'}}
@text={{t 'actions.close'}}
@icon='x'
@isIconOnly={{true}}
/>
{{/if}}
</:utilityActions>
<layout.body>
</Hds::AppHeader>
</Frame.Header>
<Frame.Sidebar>
<Hds::AppSideNav>
<Hds::AppSideNav::Portal::Target />
</Hds::AppSideNav>
</Frame.Sidebar>
<Frame.Main>
{{outlet}}
</layout.body>
</Rose::Layout::Global>
</Frame.Main>
</Hds::AppFrame>
{{#if this.flashMessages.queue}}
<div class='ember-notify-cn'>
+33 -35
View File
@@ -3,41 +3,39 @@
SPDX-License-Identifier: BUSL-1.1
}}
<main>
<BrandedCard
@title={{t 'titles.welcome-to-boundary'}}
@description={{t 'descriptions.cluster-url-initialization'}}
<BrandedCard
@title={{t 'titles.welcome-to-boundary'}}
@description={{t 'descriptions.cluster-url-initialization'}}
>
<Rose::Form
class='full-width'
@onSubmit={{fn this.setClusterUrl this.clusterUrl}}
@cancel={{false}}
@disabled={{(is-loading)}}
as |form|
>
<Rose::Form
class='full-width'
@onSubmit={{fn this.setClusterUrl this.clusterUrl}}
@cancel={{false}}
@disabled={{(is-loading)}}
as |form|
{{! template-lint-disable no-autofocus-attribute }}
<Hds::Form::TextInput::Field
name='host'
@value={{this.clusterUrl}}
autofocus
{{on 'input' (set-from-event this 'clusterUrl')}}
as |F|
>
<F.Label>{{t 'form.cluster-url.label'}}</F.Label>
<F.HelperText>{{t 'form.cluster-url.description'}}</F.HelperText>
</Hds::Form::TextInput::Field>
{{! template-lint-disable no-autofocus-attribute }}
<Hds::Form::TextInput::Field
name='host'
@value={{this.clusterUrl}}
autofocus
{{on 'input' (set-from-event this 'clusterUrl')}}
as |F|
>
<F.Label>{{t 'form.cluster-url.label'}}</F.Label>
<F.HelperText>{{t 'form.cluster-url.description'}}</F.HelperText>
</Hds::Form::TextInput::Field>
<form.actions>
<:actions>
<Hds::Button
@text={{t 'actions.submit'}}
@isFullWidth={{true}}
disabled={{form.disabled}}
type='submit'
/>
</:actions>
</form.actions>
</Rose::Form>
</BrandedCard>
</main>
<form.actions>
<:actions>
<Hds::Button
@text={{t 'actions.submit'}}
@isFullWidth={{true}}
disabled={{form.disabled}}
type='submit'
/>
</:actions>
</form.actions>
</Rose::Form>
</BrandedCard>
+10 -14
View File
@@ -5,18 +5,14 @@
{{page-title (t 'errors.generic.title')}}
<main>
<Rose::Layout::Centered>
<Error::Message @docLink={{doc-url 'support-page'}} />
</Rose::Layout::Centered>
<Rose::Layout::Centered>
<Error::Message @docLink={{doc-url 'support-page'}} />
</Rose::Layout::Centered>
<Rose::Layout::Centered>
<Hds::Button
@color='primary'
@text={{t 'actions.disconnect'}}
{{on 'click' this.appUtilities.disconnect}}
/>
</Rose::Layout::Centered>
</main>
<Rose::Layout::Centered>
<Hds::Button
@color='primary'
@text={{t 'actions.disconnect'}}
{{on 'click' this.appUtilities.disconnect}}
/>
</Rose::Layout::Centered>
+1 -28
View File
@@ -3,31 +3,4 @@
SPDX-License-Identifier: BUSL-1.1
}}
{{outlet}}
{{#if this.session.isAuthenticated}}
<EmberWormhole @to='header-nav'>
<Hds::Dropdown
class='header-dropdown-button-override'
@enableCollisionDetection={{true}}
as |dd|
>
<dd.ToggleButton
@text={{this.scopes.selectedOrg.displayName}}
@icon={{if this.scopes.selectedOrg.isGlobal 'globe' 'org'}}
@color='secondary'
/>
<dd.Interactive @route='scopes.scope' @model='global'>
{{t 'titles.global'}}
</dd.Interactive>
{{#if this.scopes.orgs.length}}
<dd.Separator />
{{/if}}
{{#each this.scopes.orgs as |scope|}}
<dd.Interactive @route='scopes.scope' @model={{scope.id}}>
{{scope.displayName}}
</dd.Interactive>
{{/each}}
</Hds::Dropdown>
</EmberWormhole>
{{/if}}
{{outlet}}
@@ -5,76 +5,72 @@
{{page-title (t 'actions.authenticate')}}
<main>
{{#if this.isOIDCRoute}}
{{outlet}}
{{else}}
<BrandedCard
@title={{t 'actions.authenticate'}}
@description={{t
'auth.descriptions.authenticating-with-org'
name=@model.scope.displayName
}}
>
{{#if this.isOIDCRoute}}
{{outlet}}
{{else}}
<BrandedCard
@title={{t 'actions.authenticate'}}
@description={{t
'auth.descriptions.authenticating-with-org'
name=@model.scope.displayName
}}
>
<Hds::Dropdown @listPosition='bottom-left' as |dd|>
<dd.ToggleButton
@text={{t 'actions.choose-different-org'}}
@icon='org'
@color='secondary'
/>
<dd.Interactive {{on 'click' (fn this.selectScope 'global' dd.close)}}>
{{t 'titles.global'}}
<Hds::Dropdown @listPosition='bottom-left' as |dd|>
<dd.ToggleButton
@text={{t 'actions.choose-different-org'}}
@icon='org'
@color='secondary'
/>
<dd.Interactive {{on 'click' (fn this.selectScope 'global' dd.close)}}>
{{t 'titles.global'}}
</dd.Interactive>
{{#each this.sortedScopes as |scope|}}
<dd.Interactive {{on 'click' (fn this.selectScope scope.id dd.close)}}>
{{scope.displayName}}
</dd.Interactive>
{{#each this.sortedScopes as |scope|}}
<dd.Interactive
{{on 'click' (fn this.selectScope scope.id dd.close)}}
>
{{scope.displayName}}
</dd.Interactive>
{{/each}}
</Hds::Dropdown>
{{/each}}
</Hds::Dropdown>
<Rose::Nav::Tabs as |nav|>
{{#each @model.authMethods as |authMethod|}}
<nav.link
@route='scopes.scope.authenticate.method'
@model={{authMethod.id}}
>
{{#if authMethod.name}}
{{authMethod.name}}
{{else}}
{{t (concat 'resources.auth-method.types.' authMethod.type)}}
{{/if}}
</nav.link>
{{/each}}
</Rose::Nav::Tabs>
<Rose::Nav::Tabs as |nav|>
{{#each @model.authMethods as |authMethod|}}
<nav.link
@route='scopes.scope.authenticate.method'
@model={{authMethod.id}}
>
{{#if authMethod.name}}
{{authMethod.name}}
{{else}}
{{t (concat 'resources.auth-method.types.' authMethod.type)}}
{{/if}}
</nav.link>
{{/each}}
</Rose::Nav::Tabs>
{{#if @model.authMethods}}
{{outlet}}
{{else}}
<Rose::Layout::Centered>
<Hds::ApplicationState data-test-no-auth-methods as |A|>
<A.Header
@icon='alert-circle'
@title={{t 'resources.auth-method.messages.none.title'}}
/>
<A.Body
@text={{t 'resources.auth-method.messages.none.description'}}
/>
</Hds::ApplicationState>
</Rose::Layout::Centered>
{{/if}}
<Hds::Text::Body @tag='p' class='change-origin'>
{{t 'cluster-url.connected-to'}}:
<Hds::Text::Code>
{{this.clusterUrl}}
</Hds::Text::Code>
<br />
<LinkTo @route='cluster-url'>
{{t 'actions.change-cluster-url'}}
</LinkTo>
</Hds::Text::Body>
</BrandedCard>
{{/if}}
</main>
{{#if @model.authMethods}}
{{outlet}}
{{else}}
<Rose::Layout::Centered>
<Hds::ApplicationState data-test-no-auth-methods as |A|>
<A.Header
@icon='alert-circle'
@title={{t 'resources.auth-method.messages.none.title'}}
/>
<A.Body
@text={{t 'resources.auth-method.messages.none.description'}}
/>
</Hds::ApplicationState>
</Rose::Layout::Centered>
{{/if}}
<Hds::Text::Body @tag='p' class='change-origin'>
{{t 'cluster-url.connected-to'}}:
<Hds::Text::Code>
{{this.clusterUrl}}
</Hds::Text::Code>
<br />
<LinkTo @route='cluster-url'>
{{t 'actions.change-cluster-url'}}
</LinkTo>
</Hds::Text::Body>
</BrandedCard>
{{/if}}
@@ -3,28 +3,26 @@
SPDX-License-Identifier: BUSL-1.1
}}
<Rose::Layout::Sidebar as |sidebarLayout|>
<sidebarLayout.sidebar>
<Rose::Nav::Sidebar as |nav|>
<nav.link @route='scopes.scope.projects.targets'>
<Hds::Icon @name='crosshair' @isInline={{true}} />
{{t 'resources.target.title_plural'}}
</nav.link>
<Hds::AppSideNav::Portal
@ariaLabel='Primary'
class='side-nav-dark-mode-overrides'
as |Nav|
>
<Nav.Link
@icon='crosshair'
@text={{t 'resources.target.title_plural'}}
@route='scopes.scope.projects.targets'
/>
<Nav.Link
@icon='entry-point'
@text={{t 'resources.session.title_plural'}}
@route='scopes.scope.projects.sessions'
/>
<Nav.Link
@icon='settings'
@text={{t 'titles.settings'}}
@route='scopes.scope.projects.settings'
/>
</Hds::AppSideNav::Portal>
<nav.link @route='scopes.scope.projects.sessions'>
<Hds::Icon @name='entry-point' @isInline={{true}} />
{{t 'resources.session.title_plural'}}
</nav.link>
<nav.link @route='scopes.scope.projects.settings'>
<Hds::Icon @name='settings' @isInline={{true}} />
{{t 'titles.settings'}}
</nav.link>
</Rose::Nav::Sidebar>
</sidebarLayout.sidebar>
<sidebarLayout.body>
<main>
{{outlet}}
</main>
</sidebarLayout.body>
</Rose::Layout::Sidebar>
{{outlet}}
+1
View File
@@ -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",
@@ -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',
);
});
@@ -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;
@@ -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}"]`);
@@ -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}"]`)
+11 -5
View File
@@ -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);
});