-
Notifications
You must be signed in to change notification settings - Fork 364
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
fix: [M3-8451] - List events in event landing page in chronological order #11339
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@linode/manager": Fixed | ||
--- | ||
|
||
Events landing page lists events in wrong order ([#11339](https://github.com/linode/manager/pull/11339)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,7 +9,6 @@ import { Hidden } from 'src/components/Hidden'; | |
import { TableCell } from 'src/components/TableCell'; | ||
import { TableRow } from 'src/components/TableRow'; | ||
import { useProfile } from 'src/queries/profile/profile'; | ||
import { getEventTimestamp } from 'src/utilities/eventUtils'; | ||
|
||
import { | ||
formatProgressEvent, | ||
|
@@ -18,16 +17,19 @@ import { | |
} from './utils'; | ||
|
||
import type { Event } from '@linode/api-v4/lib/account'; | ||
import type { DateTime } from 'luxon'; | ||
|
||
export interface completionEvent extends Event { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: I think |
||
completionTime: DateTime; | ||
} | ||
interface EventRowProps { | ||
entityId?: number; | ||
event: Event; | ||
event: completionEvent; | ||
} | ||
|
||
export const EventRow = (props: EventRowProps) => { | ||
const { event } = props; | ||
const theme = useTheme(); | ||
const timestamp = getEventTimestamp(event); | ||
const { action, message, username } = { | ||
action: event.action, | ||
message: getEventMessage(event), | ||
|
@@ -85,7 +87,7 @@ export const EventRow = (props: EventRowProps) => { | |
</TableCell> | ||
<Hidden mdDown> | ||
<TableCell data-qa-event-created-cell parentColumn="Absolute Date"> | ||
<DateTimeDisplay value={timestamp.toString()} /> | ||
<DateTimeDisplay value={event.completionTime.toString()} /> | ||
</TableCell> | ||
</Hidden> | ||
</TableRow> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,11 @@ | ||
import { within } from '@testing-library/react'; | ||
import { DateTime } from 'luxon'; | ||
import React from 'react'; | ||
|
||
import { eventFactory } from 'src/factories'; | ||
import { makeResourcePage } from 'src/mocks/serverHandlers'; | ||
import { HttpResponse, http, server } from 'src/mocks/testServer'; | ||
import { renderWithTheme } from 'src/utilities/testHelpers'; | ||
import { renderWithTheme, resizeScreenSize } from 'src/utilities/testHelpers'; | ||
|
||
import { EventsLanding } from './EventsLanding'; | ||
|
||
|
@@ -82,4 +84,69 @@ describe('EventsLanding', () => { | |
|
||
await findByText('No more events to show'); | ||
}); | ||
|
||
it('displays the data chronologically', async () => { | ||
resizeScreenSize(1600); | ||
const mockEvent1 = eventFactory.build({ | ||
action: 'linode_migrate', | ||
created: DateTime.local().minus({ minutes: 1 }).toISO(), | ||
duration: 30, | ||
entity: { | ||
id: 1, | ||
label: 'linode-1', | ||
}, | ||
status: 'finished', | ||
}); | ||
|
||
const mockEvent2 = eventFactory.build({ | ||
action: 'firewall_device_add', | ||
created: DateTime.local().minus({ minutes: 2 }).toISO(), | ||
entity: { | ||
id: 2, | ||
label: 'firewall-1', | ||
type: 'firewall', | ||
}, | ||
secondary_entity: { | ||
id: 1, | ||
label: 'linode-1', | ||
type: 'linode', | ||
}, | ||
status: 'notification', | ||
}); | ||
|
||
const eventsResponse = makeResourcePage([mockEvent1, mockEvent2], { | ||
page: 1, | ||
pages: 1, | ||
results: 1, | ||
}); | ||
|
||
server.use( | ||
http.get('*/events', () => HttpResponse.json(eventsResponse), { | ||
once: true, | ||
}), | ||
// `useEventsInfiniteQuery` needs to make two fetches to know if there are no more events to show | ||
http.get('*/events', () => HttpResponse.json(makeResourcePage([])), { | ||
once: true, | ||
}) | ||
); | ||
|
||
const { findByRole, findByText } = renderWithTheme(<EventsLanding />); | ||
await findByText('No more events to show'); | ||
|
||
const table = await findByRole('table'); | ||
const tableBodyRows = within(table).getAllByRole('row').slice(1); | ||
expect(tableBodyRows.length).toEqual(2); | ||
|
||
const dates = tableBodyRows.map((row) => { | ||
const cells = within(row).getAllByRole('cell'); | ||
return cells[3].textContent || ''; | ||
}); | ||
|
||
// util function to check if the dates are in descending order | ||
const areDatesSorted = dates.every( | ||
(date, i, arr) => i === 0 || new Date(arr[i - 1]) >= new Date(date) | ||
); | ||
|
||
expect(areDatesSorted).toBe(true); | ||
Comment on lines
+140
to
+150
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we add a test-id to the Absolute Date table cell and grab the dates that way instead of grabbing the cell at index 3? We can also just hardcode the date string; I think the test is clearer this way: const mockEvent1 = eventFactory.build({
...
created: '2024-12-10T16:29:52.973-05:00',
...
});
const mockEvent2 = eventFactory.build({
...
created: '2024-12-10T16:28:52.974-05:00',
...
});
expect(dates).toStrictEqual(['2024-12-11 05:30', '2024-12-11 05:28']); To test the sorting function, we can add a separate unit test for the |
||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,7 +21,9 @@ import { | |
StyledTableCell, | ||
StyledTypography, | ||
} from './EventsLanding.styles'; | ||
import { sortEventsChronologically } from './utils'; | ||
|
||
import type { completionEvent } from './EventRow'; | ||
import type { Filter } from '@linode/api-v4'; | ||
|
||
interface Props { | ||
|
@@ -49,6 +51,13 @@ export const EventsLanding = (props: Props) => { | |
isLoading, | ||
} = useEventsInfiniteQuery(filter); | ||
|
||
const eventsInSortedOrder: completionEvent[] = React.useMemo(() => { | ||
if (events && events.length > 0) { | ||
return sortEventsChronologically(events); | ||
} | ||
return []; | ||
}, [events]); | ||
|
||
Comment on lines
+54
to
+60
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This works! Although, it kind of hurts that we are considering client side sorting after all of the work done to simplify and streamline the events system. π I wonder if we could consider removing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that's a sensible solution, and i think with UI help it we could move towards this solution and get rid of client-side sorting. The mockup shown here is enough of a departure from the current UI to warrant UX validation from the team. it makes clear to the user all events are sorted through the timestamp, however the are a few other things to consider:
I think this could easily be solved by labelling things well and perhaps some contextual help. I'll bring this up to the UX team today |
||
const renderTableBody = () => { | ||
if (isLoading) { | ||
return ( | ||
|
@@ -70,7 +79,7 @@ export const EventsLanding = (props: Props) => { | |
} else { | ||
return ( | ||
<> | ||
{events?.map((event) => ( | ||
{eventsInSortedOrder?.map((event) => ( | ||
<EventRow | ||
entityId={entityId} | ||
event={event} | ||
|
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've extended the
Event
interface to include completionTime for an event and moved thegetEventTimeStamp()
to the sorting util function.