Icu 18518 UI add sorting support to cache daemon handler (#3168)
* feat: 🎸 add sorting support & sort feature on targets name column * test: 💍 cleanup and add tests * refactor: 💡 remove log * fix: 🐛 dont add undefined/null values from sort to query ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-18518
This commit is contained in:
1 parent
9e069ffc0c
commit
772857dbad
7 files changed
+192
-92
No files matched your search
@@ -37,6 +37,8 @@ export default class ScopesScopeProjectsTargetsIndexController extends Controlle
|
||||
{ types: { type: 'array' } },
|
||||
'page',
|
||||
'pageSize',
|
||||
'sortAttribute',
|
||||
'sortDirection',
|
||||
];
|
||||
|
||||
@tracked search;
|
||||
@@ -45,6 +47,8 @@ export default class ScopesScopeProjectsTargetsIndexController extends Controlle
|
||||
@tracked types = [];
|
||||
@tracked page = 1;
|
||||
@tracked pageSize = 10;
|
||||
@tracked sortAttribute;
|
||||
@tracked sortDirection;
|
||||
@tracked selectedTarget;
|
||||
|
||||
// =methods
|
||||
@@ -333,4 +337,16 @@ export default class ScopesScopeProjectsTargetsIndexController extends Controlle
|
||||
.then(() => this.quickConnectAndLaunchRdp(target));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets sort values and sets page to 1
|
||||
* @param {string} sortBy
|
||||
* @param {string} sortOrder
|
||||
*/
|
||||
@action
|
||||
onSort(sortBy, sortOrder) {
|
||||
this.sortAttribute = sortBy;
|
||||
this.sortDirection = sortOrder;
|
||||
this.page = 1;
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,14 @@ export default class ScopesScopeProjectsTargetsIndexRoute extends Route {
|
||||
pageSize: {
|
||||
refreshModel: true,
|
||||
},
|
||||
sortAttribute: {
|
||||
refreshModel: true,
|
||||
replace: true,
|
||||
},
|
||||
sortDirection: {
|
||||
refreshModel: true,
|
||||
replace: true,
|
||||
},
|
||||
};
|
||||
|
||||
// =methods
|
||||
@@ -97,6 +105,8 @@ export default class ScopesScopeProjectsTargetsIndexRoute extends Route {
|
||||
types,
|
||||
page,
|
||||
pageSize,
|
||||
sortAttribute,
|
||||
sortDirection,
|
||||
useDebounce,
|
||||
}) => {
|
||||
if (useDebounce) {
|
||||
@@ -122,11 +132,11 @@ export default class ScopesScopeProjectsTargetsIndexRoute extends Route {
|
||||
|
||||
const sessions = await this.getSessions(orgScope, scopes, orgFilter);
|
||||
this.addActiveSessionFilters(filters, availableSessions, sessions);
|
||||
|
||||
const sort = { attribute: sortAttribute, direction: sortDirection };
|
||||
const query = {
|
||||
recursive: true,
|
||||
scope_id: orgScope.id,
|
||||
query: { search, filters },
|
||||
query: { search, filters, sort },
|
||||
page,
|
||||
pageSize,
|
||||
force_refresh: true,
|
||||
|
||||
@@ -81,108 +81,119 @@
|
||||
|
||||
{{#if @model.targets}}
|
||||
<Hds::Table
|
||||
@model={{@model.targets}}
|
||||
@columns={{array
|
||||
(hash label=(t 'form.name.label'))
|
||||
(hash label=(t 'resources.alias.title_plural'))
|
||||
(hash label=(t 'resources.target.titles.active-sessions'))
|
||||
(hash label=(t 'form.type.label'))
|
||||
(hash label=(t 'resources.project.title'))
|
||||
(hash label=(t 'titles.actions') isVisuallyHidden=true align='right')
|
||||
}}
|
||||
@onSort={{this.onSort}}
|
||||
@sortBy={{this.sortAttribute}}
|
||||
@sortOrder={{this.sortDirection}}
|
||||
@valign='middle'
|
||||
>
|
||||
<:head as |H|>
|
||||
<H.Tr>
|
||||
<H.ThSort
|
||||
@onClickSort={{fn H.setSortBy 'name'}}
|
||||
@sortOrder={{if (eq 'name' H.sortBy) H.sortOrder}}
|
||||
>{{t 'form.name.label'}}</H.ThSort>
|
||||
<H.Th>{{t 'resources.alias.title_plural'}}</H.Th>
|
||||
<H.Th>{{t 'resources.target.titles.active-sessions'}}</H.Th>
|
||||
<H.Th>{{t 'form.type.label'}}</H.Th>
|
||||
<H.Th>{{t 'resources.project.title'}}</H.Th>
|
||||
<H.Th @isVisuallyHidden={{true}} @align='right'>{{t
|
||||
'titles.actions'
|
||||
}}</H.Th>
|
||||
</H.Tr>
|
||||
</:head>
|
||||
<:body as |B|>
|
||||
<B.Tr>
|
||||
<B.Td>
|
||||
<Hds::Text::Body @weight='semibold'>
|
||||
{{#if (can 'read model' B.data)}}
|
||||
<LinkTo
|
||||
data-test-visit-target={{B.data.id}}
|
||||
@route='scopes.scope.projects.targets.target'
|
||||
@model={{B.data.id}}
|
||||
@query={{hash isConnecting=false}}
|
||||
>
|
||||
{{B.data.displayName}}
|
||||
</LinkTo>
|
||||
{{else}}
|
||||
{{B.data.displayName}}
|
||||
{{#each @model.targets as |target|}}
|
||||
<B.Tr>
|
||||
<B.Td>
|
||||
<Hds::Text::Body @weight='semibold'>
|
||||
{{#if (can 'read model' target)}}
|
||||
<LinkTo
|
||||
data-test-visit-target={{target.id}}
|
||||
@route='scopes.scope.projects.targets.target'
|
||||
@model={{target.id}}
|
||||
@query={{hash isConnecting=false}}
|
||||
>
|
||||
{{target.displayName}}
|
||||
</LinkTo>
|
||||
{{else}}
|
||||
{{target.displayName}}
|
||||
{{/if}}
|
||||
{{#if target.description}}
|
||||
<Hds::Text::Body @tag='p'>
|
||||
{{target.description}}
|
||||
</Hds::Text::Body>
|
||||
{{/if}}
|
||||
</Hds::Text::Body>
|
||||
</B.Td>
|
||||
<B.Td data-test-target-aliases={{target.id}}>
|
||||
{{truncate-list 'actions.more' target.associatedAliases}}
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
{{#if target.isActive}}
|
||||
<Hds::Button
|
||||
data-test-targets-sessions-flyout-button={{target.id}}
|
||||
@text={{t 'actions.yes'}}
|
||||
@color='tertiary'
|
||||
@icon='info'
|
||||
{{on 'click' (fn this.selectTarget target)}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{#if B.data.description}}
|
||||
<Hds::Text::Body @tag='p'>
|
||||
{{B.data.description}}
|
||||
</Hds::Text::Body>
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
{{#if target.type}}
|
||||
<Hds::Badge
|
||||
@text={{t (concat 'resources.target.types.' target.type)}}
|
||||
/>
|
||||
{{/if}}
|
||||
</Hds::Text::Body>
|
||||
</B.Td>
|
||||
<B.Td data-test-target-aliases={{B.data.id}}>
|
||||
{{truncate-list 'actions.more' B.data.associatedAliases}}
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
{{#if B.data.isActive}}
|
||||
<Hds::Button
|
||||
data-test-targets-sessions-flyout-button={{B.data.id}}
|
||||
@text={{t 'actions.yes'}}
|
||||
@color='tertiary'
|
||||
@icon='info'
|
||||
{{on 'click' (fn this.selectTarget B.data)}}
|
||||
/>
|
||||
{{/if}}
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
{{#if B.data.type}}
|
||||
<Hds::Badge
|
||||
@text={{t (concat 'resources.target.types.' B.data.type)}}
|
||||
/>
|
||||
{{/if}}
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
<div>
|
||||
<Hds::Copy::Snippet
|
||||
@textToCopy={{B.data.project.id}}
|
||||
@color='secondary'
|
||||
data-test-target-project-id={{B.data.project.id}}
|
||||
/>
|
||||
</div>
|
||||
{{#if B.data.project.name}}
|
||||
<Hds::Badge @color='neutral' @text={{B.data.project.name}} />
|
||||
{{/if}}
|
||||
</B.Td>
|
||||
<B.Td @align='right'>
|
||||
<div {{style display='inline-block'}}>
|
||||
{{#if (can 'connect target' B.data)}}
|
||||
{{#if (can 'read target' B.data)}}
|
||||
{{#if (and B.data.isRDP this.rdp.isPreferredRdpClientSet)}}
|
||||
<Hds::Button
|
||||
data-test-targets-open-button={{B.data.id}}
|
||||
@text={{t 'actions.open'}}
|
||||
@icon='external-link'
|
||||
@iconPosition='trailing'
|
||||
@color='secondary'
|
||||
{{on 'click' (fn this.quickConnectAndLaunchRdp B.data)}}
|
||||
/>
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
<div>
|
||||
<Hds::Copy::Snippet
|
||||
@textToCopy={{target.project.id}}
|
||||
@color='secondary'
|
||||
data-test-target-project-id={{target.project.id}}
|
||||
/>
|
||||
</div>
|
||||
{{#if target.project.name}}
|
||||
<Hds::Badge @color='neutral' @text={{target.project.name}} />
|
||||
{{/if}}
|
||||
</B.Td>
|
||||
<B.Td @align='right'>
|
||||
<div {{style display='inline-block'}}>
|
||||
{{#if (can 'connect target' target)}}
|
||||
{{#if (can 'read target' target)}}
|
||||
{{#if (and target.isRDP this.rdp.isPreferredRdpClientSet)}}
|
||||
<Hds::Button
|
||||
data-test-targets-open-button={{target.id}}
|
||||
@text={{t 'actions.open'}}
|
||||
@icon='external-link'
|
||||
@iconPosition='trailing'
|
||||
@color='secondary'
|
||||
{{on 'click' (fn this.quickConnectAndLaunchRdp target)}}
|
||||
/>
|
||||
{{else}}
|
||||
<Hds::Button
|
||||
data-test-targets-connect-button={{target.id}}
|
||||
@text={{t 'resources.session.actions.connect'}}
|
||||
@color='secondary'
|
||||
@route='scopes.scope.projects.targets.target'
|
||||
@model={{target.id}}
|
||||
@query={{hash isConnecting=true}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<Hds::Button
|
||||
data-test-targets-connect-button={{B.data.id}}
|
||||
data-test-targets-connect-button={{target.id}}
|
||||
@text={{t 'resources.session.actions.connect'}}
|
||||
@color='secondary'
|
||||
@route='scopes.scope.projects.targets.target'
|
||||
@model={{B.data.id}}
|
||||
@query={{hash isConnecting=true}}
|
||||
{{on 'click' (fn this.quickConnect target)}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<Hds::Button
|
||||
data-test-targets-connect-button={{B.data.id}}
|
||||
@text={{t 'resources.session.actions.connect'}}
|
||||
@color='secondary'
|
||||
{{on 'click' (fn this.quickConnect B.data)}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</B.Td>
|
||||
</B.Tr>
|
||||
</div>
|
||||
</B.Td>
|
||||
</B.Tr>
|
||||
{{/each}}
|
||||
</:body>
|
||||
</Hds::Table>
|
||||
{{#if @model.isLoadIncomplete}}
|
||||
|
||||
@@ -161,6 +161,10 @@ const searchCliCommand = (requestData) => {
|
||||
if (requestData.filter) {
|
||||
searchCommand.push(`-filter=${requestData.filter}`);
|
||||
}
|
||||
if (requestData.sort_by && requestData.sort_direction) {
|
||||
searchCommand.push(`-sort-by=${requestData.sort_by}`);
|
||||
searchCommand.push(`-sort-direction=${requestData.sort_direction}`);
|
||||
}
|
||||
if (requestData.force_refresh) {
|
||||
searchCommand.push(`-force-refresh=true`);
|
||||
}
|
||||
|
||||
@@ -49,6 +49,12 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
const TARGET_OPEN_BUTTON = (id) => `[data-test-targets-open-button="${id}"]`;
|
||||
const TARGET_CONNECT_BUTTON = (id) =>
|
||||
`[data-test-targets-connect-button="${id}"]`;
|
||||
const TABLE_SORT_BTN = 'thead tr th:nth-child(1) button';
|
||||
const TABLE_SORT_BTN_ARROW_UP =
|
||||
'thead tr th:nth-child(1) button .hds-icon-arrow-up';
|
||||
const TABLE_SORT_BTN_ARROW_DOWN =
|
||||
'thead tr th:nth-child(1) button .hds-icon-arrow-down';
|
||||
|
||||
const instances = {
|
||||
scopes: {
|
||||
global: null,
|
||||
@@ -874,4 +880,37 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
// Assert that the confirm modal appears
|
||||
assert.dom(HDS_DIALOG_MODAL).isVisible();
|
||||
});
|
||||
|
||||
test('sorting by target name updates url', async function (assert) {
|
||||
this.stubCacheDaemonSearch(
|
||||
'sessions',
|
||||
'targets',
|
||||
'aliases',
|
||||
'sessions',
|
||||
|
||||
'sessions',
|
||||
'targets',
|
||||
'aliases',
|
||||
'sessions',
|
||||
|
||||
'sessions',
|
||||
'targets',
|
||||
'aliases',
|
||||
'sessions',
|
||||
);
|
||||
await visit(urls.projects);
|
||||
|
||||
await click(`[href="${urls.targets}"]`);
|
||||
await click(TABLE_SORT_BTN);
|
||||
|
||||
assert.true(currentURL().includes('sortAttribute=name'));
|
||||
assert.true(currentURL().includes('sortDirection=asc'));
|
||||
assert.dom(TABLE_SORT_BTN_ARROW_UP).isVisible();
|
||||
|
||||
await click(TABLE_SORT_BTN);
|
||||
|
||||
assert.true(currentURL().includes('sortAttribute=name'));
|
||||
assert.true(currentURL().includes('sortDirection=desc'));
|
||||
assert.dom(TABLE_SORT_BTN_ARROW_DOWN).isVisible();
|
||||
});
|
||||
});
|
||||
@@ -256,5 +256,17 @@ module(
|
||||
|
||||
assert.strictEqual(session.status, STATUS_SESSION_CANCELING);
|
||||
});
|
||||
|
||||
test('onSort action sets expected values correctly', async function (assert) {
|
||||
this.stubCacheDaemonSearch();
|
||||
assert.notOk(controller.sortAttribute);
|
||||
assert.notOk(controller.sortDirection);
|
||||
|
||||
controller.onSort('name', 'desc');
|
||||
|
||||
assert.strictEqual(controller.page, 1);
|
||||
assert.strictEqual(controller.sortAttribute, 'name');
|
||||
assert.strictEqual(controller.sortDirection, 'desc');
|
||||
});
|
||||
},
|
||||
);
|
||||
Reference in new issue
Block a user