-
Notifications
You must be signed in to change notification settings - Fork 8.2k
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
[Lens] New sorting feature for the datatable visualization #84435
Conversation
@elasticmachine merge upstream |
Pinging @elastic/kibana-app (Team:KibanaApp) |
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.
Tested in Chrome and it's working consistently with Visualize. Accessibility looks good.
Can you think about how the title of this PR would appear in release notes, and whether we need to document this as part of the existing docs?
The "download as CSV" was not sorted- it makes sense, but is there something we can do to improve this?
let sortedRows = rows; | ||
|
||
if (sortBy && sortDirection !== 'none') { | ||
sortedRows = orderBy(rows, [sortBy], sortDirection as Direction); |
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 we are sorting based on the unformatted value of the table cell, not the formatted value. I checked and this is the existing behavior of the Visualize app, but is this the best behavior? It does appear to be correct for numbers and dates using the standard formatters.
The buggiest case I could find is how we deal with IP address fields- the sort order looks totally wrong because it's sorting the unformatted value. What do you think the options are?
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.
This is a good point. I'll improve on this side. 👍
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.
@dej611 I would expect numbers and dates to be sorted by raw value and strings sorted by the formatted value (e.g. for stuff like static lookup formatters) .
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.
What about other types? Using formatters for everything but numbers and dates?
sorting?: { | ||
columnId: string | undefined; | ||
direction: 'asc' | 'desc'; | ||
}; |
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.
Why is the sorting attached to the top level of the state, instead of attached to a specific layer?
Also, what happens in these two cases?
- A sorted column is deleted- is the state correctly updated?
- The columnId for a sorted column is reused but the function has changed. It's still sorted, but should it be?
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.
Yes, I've thought about that and discussed a bit with @flash1293 about.
We agreed to have sorting on a visualization level for now, as tables can only have one layer.
When we'll extend tables support for multiple layer this feature will be revisited, as probably other changes are planned to happen to this code meanwhile.
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.
No strong feeling about the sorting state - @dej611 and me discussed this but I think it's fine in both places. I wasn't aware we are supporting multiple layers in the first place, what's the reasoning for this? (it seems like it's not possible for the user to actually configure this and even if they did, it would just show the first table)
The columnId for a sorted column is reused but the function has changed. It's still sorted, but should it be?
IMHO it should still be sorted as long as it's the same dimension/column. If the dimension gets deleted completely, the sorting state should be deleted along with it.
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.
- A sorted column is deleted- is the state correctly updated?
Probably the state in this case will remain "dirty" but the final result should be correct anyway as it relies on the columnId
. I'll try to address this scenario.
- The columnId for a sorted column is reused but the function has changed. It's still sorted, but should it be?
I wasn't aware of recycled ids
. I'll take care for that too.
I think it still makes sense to have the sorting on that column, even when the operation is changed.
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.
Had a first look at this implementation: the formatters are available only at render time as dependency, not at expression time, going in conflict on the CSV sorted downloadable content.
I'll investigate on possible solutions. In case I'd rather prioritise this over the CSV sorted downloadable content.
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.
Ah, good point. Agreed, we can open a separate issue for CSV sorted downloadable content. It's fixable by passing down the reference to the format factory from data plugin from the setup
method of the DatatableVisualization
service class in x-pack/plugins/lens/public/datatable_visualization/index.ts
and wrapping the datatable visualization definition into a getter (getDatatableVisualizationDefinition(data: DataPublicPluginStart)
). We are doing a similar thing in xy chart for passing down the palette service.
x-pack/plugins/lens/public/types.ts
Outdated
@@ -608,6 +608,17 @@ export interface LensBrushEvent { | |||
data: TriggerContext<typeof SELECT_RANGE_TRIGGER>['data']; | |||
} | |||
|
|||
export interface LensSortActionData { |
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 you move this type into the datatable visualization and make LensEditEvent
a generic type for all kinds of payloads?
That's a good point - we can actually improve this by moving the sorting logic into the datatable expression function instead of the renderer and updating the |
… into feature/lens/table-sorting
Implemented a basic readOnly state for the sorting header. As an alternative I've also tried with a tooltip when in readOnly mode: it offers a cleaner solution, but then introduces inconsistencies with the edit mode. Unless we want to completely handle the sorting feature via custom tooltips |
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 would be fine with this solution a11y-wise, but I would like to get @myasonik s expertise on this:
Michail, we want to show a sorted table and indicate that, but the user shouldn't be able to change the sorting. Unfortunately this is currently not supported by EuiBasicTable
.
This PR implements a workaround by not telling the table it's sorting, but showing the icon and a screenreader text in the header cell manually.
Header cell during edit mode (default created by EUI component):
The only thing missing AFAICT is the aria-sort
attribute on the th
element (it's on the span
within the th
, but not sure whether it helps much there). The question is whether that's a big no-no or whether it's just not perfect. If it's a minor issue, we would like to get the feature into 7.11, otherwise we will take care of an upstream fix in EUI to do it right from the start.
You can test this by creating a Lens table and clicking the header to sort the table, then saving it to a dashboard.
x-pack/plugins/lens/public/types.ts
Outdated
@@ -25,6 +25,7 @@ import { | |||
VALUE_CLICK_TRIGGER, | |||
VisualizeFieldContext, | |||
} from '../../../../src/plugins/ui_actions/public'; | |||
import { LensSortActionData, LENS_EDIT_SORT_ACTION } from './datatable_visualization/expression'; |
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.
This import is causing the current build to fail because all of the datatable vis is included in the initial bundle. This should be fixable by making it a type import: import type { LensSortActionData, LENS_EDIT_SORT_ACTION } from './datatable_visualization/expression';
} | ||
// This is a workaround to hijack the title value of the header cell | ||
return ( | ||
<span aria-sort={getDirectionLongLabel(sorting.direction)} aria-live="polite"> |
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.
This won't update, so no need to make it aria-live="polite"
columnsReverseLookup[sortBy]?.meta?.type || '' | ||
) | ||
? sortBy | ||
: (row: Record<string, unknown>) => formatters[sortBy]?.convert(row[sortBy]); |
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.
This is not taking into account ip sorting, right? If Visualize datatable isn't doing it right either at the moment, I'm fine with resolving this later, but we should create an issue for it.
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.
Update a first version of IPv4 sorting specific logic
IMO it's a minor issue. Screenreaders will read out the table sort better* when using As another workaround which may be uglier in the code but would be better for a11y: you could use a *What does better mean?Better just means assistive tech can/will read out the sort when entering the table from anywhere (jumping into it, going backwards into it, etc), whereas with the current solution a user wouldn't know the table is sorted without reading the column header. |
This scares me, if we can live without it please let us do :) @dej611 @wylieconlon I'd say let's try to get this merged before 7.11 as it seems very close to me. I can give it another look tomorrow. |
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.
As discussed, the logic for changing the sort order should be moved inside of the datatable visualization, keeping the frame level code agnostic to specific events being fired.
Also, visualizationState
should not re-create the handleEvent
callback (this can be done by allowing an updater function to be used for visualization)
if (type !== 'ip') { | ||
return rowFormatted; | ||
} | ||
// Note this supports only IPv4: maybe there already a solution to support IPv6 and more? |
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.
Ip fields can also store ipv6 values in Elasticsearch: https://www.elastic.co/guide/en/elasticsearch/reference/current/ip.html
To me it feels weird to just support ip4, but always attempt to sort. IMHO we should split that feature out and disable sorting on ip columns for now and tackle that separately.
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.
Yes, agree. There are also special values we may want to handle (localhost
as/or 127.0.0.1
?).
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.
This looks great to me!
The only thing I'm concerned about is the ip sorting, I left a comment about that.
Otherwise this LGTM.
Edit: Just discovered another edge case where it doesn't work correctly - ranges:
I think they should get sorted by the from
, then by the to
, but I'm not a fan of tying the datatable sorting algorithm to ranges specifically. I can't think of a better solution though - so for now I would be fine with checking whether a number
column contains objects with a from
or a to
property and sort by them.
Tried to implement this but it's not super trivial. I would rather keep the string comparison for the moment and probably build/get a tiny utility well tested for that. I could still remove the IPv4 sorting logic if prefer to delay for a better solution. |
I would go with the following:
|
@elasticmachine merge upstream |
@elasticmachine merge upstream |
As discussed offline, for the first version we will just allow sorting ips and ranges and sort them as strings. Improving this will become a fix-it-week issue. |
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.
Tested and everything works as expected, LGTM.
#83167 changed some types in the |
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.
Tested the new behavior and LGTM, left some style comments
state: | ||
typeof action.updater === 'function' | ||
? action.updater(state.visualization.state) | ||
: action.updater, |
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.
This is a good change, but does it need to be in this PR specifically?
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.
This PR was the place we realised about this possible change. Making it here helped fixing a useEffect
side effect which was affecting the datatable visualization.
...ck/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx
Outdated
Show resolved
Hide resolved
…orkspace_panel/workspace_panel.tsx Co-authored-by: Wylie Conlon <[email protected]>
💚 Build SucceededMetrics [docs]Async chunks
Distributable file count
Page load bundle
History
To update your PR or re-run it, just comment with: |
…4435) Co-authored-by: Wylie Conlon <[email protected]> Co-authored-by: Kibana Machine <[email protected]>
) (#85987) Co-authored-by: Wylie Conlon <[email protected]> Co-authored-by: Kibana Machine <[email protected]>
Summary
Fixes #76962
This PR implements the sorting feature for the datatable visualization in Lens.
Rather than using the Canvas
sort
function the lodash sorting implementation has been used in here: there's a compatibility issue between theMultiDatatable
format and theDatatable
accepted as input.The feature reads the
renderMode
property to become interactive, and its stored in the state to be shown when indisplay
mode.Styling can probably be improved: a feature request has been submitted for a new
readOnly
state for Table sorting, in order to improve the View mode look and feel.Checklist