-
Notifications
You must be signed in to change notification settings - Fork 4.4k
UI: Replace Client Count Attribution Charts with Table #30678
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
UI: Replace Client Count Attribution Charts with Table #30678
Conversation
Build Results: |
CI Results: |
</:body> | ||
</Hds::Table> | ||
|
||
<Hds::Pagination::Numbered |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[suggestion] according to Helios guidance there should be a margin of 16px between the table and the pagination: https://helios.hashicorp.design/components/pagination#spacing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! I've updated the margin accordingly 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Amazing job tackling this! So cool this will make it into the release. I definitely think moving this to a child component makes sense here. I went back and forth on the query param, but ultimately think it overcomplicates the logic a little more than necessary. Let me know if you want to discuss the approach 😄
Really great job building client count context and addressing these improvements ✨
<B.Td>{{B.data.namespace}}</B.Td> | ||
<B.Td>{{B.data.label}}</B.Td> | ||
{{#if (eq B.data.mount_type "deleted mount")}} | ||
<B.Td><Hds::Badge @text="Disabled" @type="outlined" /></B.Td> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there was discussion to update the display text to "Deleted" to clarify what disabled means in the api context
changelog/30678.txt
Outdated
@@ -0,0 +1,3 @@ | |||
```release-note:change | |||
ui: (Client Counts) The mount attribution and namespace attribution charts have been replaced in the overview tab with a table that breaks down client attribution according to month selection. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use the same language and formatting as backend changelog entries (example) to denote which aspect of Vault this applies to? ui/activity
instead of ui: (Client Counts)
Also a slight rewording suggestion to explain in a little more detail what the table functionality includes.
ui/activity: Replaces mount and namespace attribution charts with a table to allow sorting
client count data by `namespace`, `mount_path`, `mount_type` or number of clients for
a selected month.
@@ -142,9 +138,11 @@ export default class ClientsDateRangeComponent extends Component<Args> { | |||
// used for CE date picker | |||
@action handleSave() { | |||
if (this.validationError) return; | |||
// including the month param here as undefined allows the table month selection to reset when a new billing period is chosen |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems a little strange to do this here, since this is a query param it should probably be reset when the route refreshes
}} | ||
@sortBy="clients" | ||
@sortOrder="desc" | ||
@onSort={{this.updateSort}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is a custom sort function necessary? All of these columns seem like they can either be sortable alphabetically or numerically (docs).
If this is just for the deleted
status...I'd consider disregarding that since they'll technically still be grouped together. I think a follow-on improvement would be eventually removing isSortable
for mount_type
all together and instead adding that as a filter option for the table data. That way users could filter by a certain type, or filter out deleted mounts. (Since sorting doesn't really seem useful for that data key).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added the custom sort here to support paginating the data. Otherwise, with the table native sort, it would only sort the current page. I'll see if I can rework the pagination approach so that we can remove this sort function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't end up seeing a way to support pagination and sorting, without the extra custom sort function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, HDS table sorts only the visible rows, not the entire dataset. FYI there are UX implications when sorting the entire dataset for a paginated table, even more if it's filtered, and we wanted to stay out of this and let the consumers handle this kind of logic in the context of what they need/know.
export default class ClientsCountsController extends Controller { | ||
queryParams = queryParamKeys; | ||
|
||
start_time: string | number | undefined = undefined; | ||
end_time: string | number | undefined = undefined; | ||
ns: string | undefined = undefined; | ||
mountPath: string | undefined = undefined; | ||
month: string | undefined = undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Normally I would say that using a query param here is the right move because it's filtering client-side data, however it's a little confusing since the month
only applies to the table and not the entire page. I think it'd make sense to put here if the route data was going to be filtered by that month, but since it's just the table - I think this should move down to the component and just be managed there 🤔
{{/each}} | ||
</S.Options> | ||
</Hds::Form::Select::Base> | ||
{{#if this.tableData}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this logic be pulled into a separate component, something like Clients::Table
? That would make the template easier to parse and separate concerns a little more 🤔 In the future it could be abstracted to accept an arg for column keys, but I think for now most of this could be moved 1 to 1.
Per my suggestion below to remove the query param, this could then be simplified to just check for whether or not a month has been selected in the parent. The child component would then show the table or empty state accordingly
{{! Clients::Table component }}
{{#if @filteredMonth}}
{{! HDS Table }}
{{else}}
{{! Empty state }}
{{/if}}
: 'View the namespace mount breakdown of clients by selecting a month.'; | ||
} | ||
|
||
sortTableData(data: TableData[]): TableData[] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The table can sort these for us, but then we don't have access to the sorted data to subsequently paginate. This sort function allows us to sort the whole table and then paginate off of that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense! I figured the pagination would add some fun to this 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a few more cleanup items! Can the clients/attribution
component also be deleted? I don't think it's used elsewhere. It would be good to add tests for the new table component, but that can be a follow-on since I know we want to get this in before feature freeze.
Accurately updating the clients response and adding a test can happen in follow-ons, but it'd be great to cleanup the remaining tidy items from removing the query param before merging. Great work!
@isSecretsSyncActivated={{this.flags.secretsSyncIsActivated}} | ||
/> | ||
{{/if}} | ||
<Hds::Form::Select::Base |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Much cleaner and easier to follow! 🎉
import { HTMLElementEvent } from 'vault/forms'; | ||
import { parseAPITimestamp } from 'core/utils/date-formatters'; | ||
import { MountClients } from 'core/utils/client-count-utils'; | ||
import RouterService from '@ember/routing/router-service'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I for any types imports can type
be included? I also think it's nice to group them together, so all services are together
import RouterService from '@ember/routing/router-service'; | |
import type RouterService from '@ember/routing/router-service'; |
: 'View the namespace mount breakdown of clients by selecting a month.'; | ||
} | ||
|
||
sortTableData(data: TableData[]): TableData[] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense! I figured the pagination would add some fun to this 😅
namespaces?.forEach((n) => { | ||
const mounts: TableData[] = n.mounts.map((m) => { | ||
// add namespace to mount block | ||
return { ...m, namespace: n.label }; | ||
}); | ||
data = [...data, ...mounts]; | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
technically this sort of data manipulation should happen in the serializer, in the client-count-utils (And this getter would just select the month). I know it was here for the POC pr 🙃 - is it a lot of lift to move? That can happen after feature freeze if that's easiest.
ui/app/components/clients/table.ts
Outdated
} | ||
|
||
get tableHeaderMessage(): string { | ||
return this.args.filteredMonth |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like filteredMonth
could be removed all together and this could be updated to this.args.data.length
?
{{/each}} | ||
</S.Options> | ||
</Hds::Form::Select::Base> | ||
<Clients::Table @data={{this.tableData}} @filteredMonth={{this.selectedMonth}} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the component - could filteredMonth
be removed and the component just check for @data
?
@@ -10,4 +10,5 @@ | |||
@endTimestamp={{this.model.endTimestamp}} | |||
@namespace={{this.countsController.ns}} | |||
@mountPath={{this.countsController.mountPath}} | |||
@updateQueryParams={{this.countsController.updateQueryParams}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be deleted?
export interface OnChangeParams { | ||
start_time?: number | undefined; | ||
end_time?: number | undefined; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is no longer used other places, I wonder if moving it back to the component makes the most sense 🤔
await render( | ||
hbs`<Clients::Page::Overview @activity={{this.activity}} @namespace="ns1" @mountPath={{this.mountPath}} />` | ||
hbs`<Clients::Page::Overview @activity={{this.activity}} @updateQueryParams={{this.onChange}}/>` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updateQueryParams
can this arg be removed from all the tests?
@@ -56,6 +57,7 @@ export const ACTIVITY_RESPONSE_STUB = { | |||
}, | |||
{ | |||
mount_path: 'kvv2-engine-0', | |||
mount_type: '', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for testing accuracy, it'd be good to update these to what they actually are instead of empty strings.
mounts = ns.mounts.map((m) => ({ label: m.mount_path, ...destructureClientCounts(m.counts) })); | ||
mounts = ns.mounts.map((m) => ({ | ||
label: m.mount_path, | ||
mount_type: m.mount_type || '', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the ||
may not be necessary, since it looks like the API should return a placeholder string if it's empty https://github.com/hashicorp/vault/pull/30071/files#diff-9617be4433471ef034fc9f806398327dcf6241dd6945bdd7a61202193179f922R421-R422
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, good find!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚀 Can tests be added in a follow-on?
It'd be great to make sure the table renders as expected, sorts and paginates. Plus that the empty state views show as expected 😄
There are currently some table tests in |
* replace attribution charts with a table by month * update tests to include mount_type * fix another portion of tests that were missing secret sync stat and testing for old attribution charts * add tests for attribution table * add changelog and tidy * remove remaining todos * tidy * reset month query param in ce * fix tests missing month param * add margin to pagination in accordance to helios rec * remove query param, update change log, move table into own comp * remove commented code * remove month query params * tidy * update test mount paths * remove unused client attribution component * update tests
Description
This PR removes the mount attribution and namespace attribution charts from the overview tab. They are replaced by a table which breaks down attribution according to a selected month.
Ent tests: ✅
Table state w/ no month selection:

Table state w/ a month selected that has no data:

Table state w/ a month selected that has data:

TODO only if you're a HashiCorp employee
backport/
label that matches the desired release branch. Note that in the CE repo, the latest release branch will look likebackport/x.x.x
, but older release branches will bebackport/ent/x.x.x+ent
.of a public function, even if that change is in a CE file, double check that
applying the patch for this PR to the ENT repo and running tests doesn't
break any tests. Sometimes ENT only tests rely on public functions in CE
files.
in the PR description, commit message, or branch name.
description. Also, make sure the changelog is in this PR, not in your ENT PR.