Repository navigation
upcoming: [DI-23640] - Added table in alert contextual view #11734
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
Merged
nikhagra-akamai
merged 25 commits into
linode:develop
from
nikhagra-akamai:alerts/context-view
Mar 5, 2025
Merged
Changes from all commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
fe5e6e5
upcoming: [DI-23311] - Added reusable component for alerts contextualβ¦
nikhagra-akamai 819dd5b
added changeset
nikhagra-akamai 9897792
upcoming:[DI-23311] - Remove unused variable
nikhagra-akamai a276828
Merge branch 'develop' of github.com:linode/manager into alerts/conteβ¦
nikhagra-akamai c9ba840
upcoming: [DI-23548] - Updated test case
nikhagra-akamai 8b6e0ca
upcoming: [DI-23548] - Remove unused code
nikhagra-akamai 2326c15
upcoming: [DI-23548] - Hide label in search & autocomplete
nikhagra-akamai 97dc48b
upcoming: [DI-23548] - Removed unused variable
nikhagra-akamai 1ed1b70
Merge branch develop of github.com:linode/manager into alerts/contextβ¦
nikhagra-akamai a16186e
upcoming: [DI-23548] - Updated button type to outlined
nikhagra-akamai 1808d31
Merge branch 'develop' of github.com:linode/manager into alerts/conteβ¦
nikhagra-akamai 3621f00
upcoming: [DI-23640] - Added table in alert contextual view
nikhagra-akamai a473fc5
Merge branch develop of github.com:linode/manager into alerts/contextβ¦
nikhagra-akamai 0df6220
added changeset
nikhagra-akamai 7422394
upcoming: [DI-23640] - Replace null component with hyphen
nikhagra-akamai dd6e8ac
upcoming: [DI-23640] - Removed unused variables
nikhagra-akamai b472352
upcoming: [DI-23640] - Removed unused imports
nikhagra-akamai 5eae251
upcoming: [DI-23640] - Updated chip styling
nikhagra-akamai 6acb83c
Merge branch 'develop' of github.com:linode/manager into alerts/conteβ¦
nikhagra-akamai 9fa05eb
upcoming: [DI-23640] - Updated test cases
nikhagra-akamai 19bd613
Added testing file
nikhagra-akamai d2684ae
upcoming: [DI-23640] - Updated review comments
nikhagra-akamai 085aff1
upcoming: [DI-23640] - Updated color for chip component
nikhagra-akamai 2de5899
reverted index file
nikhagra-akamai d270be7
Merge branch 'develop' of github.com:linode/manager into alerts/conteβ¦
nikhagra-akamai File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
5 changes: 5 additions & 0 deletions
5
packages/manager/.changeset/pr-11734-upcoming-features-1740564048829.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@linode/manager": Upcoming Features | ||
| --- | ||
|
|
||
| Update `AlertReusableComponent` to add table, Add `AlertInformationActionTable` and `AlertInformationActionRow` compoenent ([#11734](https://github.com/linode/manager/pull/11734)) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
.../manager/src/features/CloudPulse/Alerts/ContextualView/AlertInformationActionRow.test.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| import { capitalize } from '@linode/utilities'; | ||
| import React from 'react'; | ||
|
|
||
| import { alertFactory } from 'src/factories'; | ||
| import { renderWithTheme } from 'src/utilities/testHelpers'; | ||
|
|
||
| import { processMetricCriteria } from '../Utils/utils'; | ||
| import { AlertInformationActionRow } from './AlertInformationActionRow'; | ||
|
|
||
| describe('Alert list table row', () => { | ||
| it('Should display the data', () => { | ||
| const alert = alertFactory.build(); | ||
|
|
||
| const { getByText } = renderWithTheme( | ||
| <AlertInformationActionRow | ||
| alert={alert} | ||
| handleToggle={vi.fn()} | ||
| status={true} | ||
| /> | ||
| ); | ||
|
|
||
| expect(getByText(alert.label)).toBeInTheDocument(); | ||
| expect(getByText(capitalize(alert.type))).toBeInTheDocument(); | ||
| }); | ||
|
|
||
| it('Should display metric threshold', () => { | ||
| const alert = alertFactory.build(); | ||
| const processCriteria = processMetricCriteria(alert.rule_criteria.rules)[0]; | ||
| const { getByText } = renderWithTheme( | ||
| <AlertInformationActionRow | ||
| alert={alert} | ||
| handleToggle={vi.fn()} | ||
| status={false} | ||
| /> | ||
| ); | ||
| expect( | ||
| getByText( | ||
| `${processCriteria.label} ${processCriteria.metricOperator} ${processCriteria.threshold} ${processCriteria.unit}` | ||
| ) | ||
| ).toBeInTheDocument(); | ||
| }); | ||
|
|
||
| it('Should have toggle button disabled', () => { | ||
| const alert = alertFactory.build(); | ||
| const { getByRole } = renderWithTheme( | ||
| <AlertInformationActionRow | ||
| alert={alert} | ||
| handleToggle={vi.fn()} | ||
| status={true} | ||
| /> | ||
| ); | ||
|
|
||
| expect(getByRole('checkbox')).toHaveProperty('checked'); | ||
| }); | ||
| }); |
68 changes: 68 additions & 0 deletions
68
packages/manager/src/features/CloudPulse/Alerts/ContextualView/AlertInformationActionRow.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| import { FormControlLabel, Toggle, Typography } from '@linode/ui'; | ||
| import { capitalize } from '@linode/utilities'; | ||
| import React from 'react'; | ||
|
|
||
| import { Link } from 'src/components/Link'; | ||
| import { TableCell } from 'src/components/TableCell'; | ||
| import { TableRow } from 'src/components/TableRow'; | ||
|
|
||
| import { processMetricCriteria } from '../Utils/utils'; | ||
| import { MetricThreshold } from './MetricThreshold'; | ||
|
|
||
| import type { Alert } from '@linode/api-v4'; | ||
|
|
||
| interface AlertInformationActionRowProps { | ||
| /** | ||
| * Alert object which should be dispalyed in the row | ||
| */ | ||
| alert: Alert; | ||
|
|
||
| /** | ||
| * Handler function for the click of toggle button | ||
| * @param alert object for which toggle button is click | ||
| */ | ||
| handleToggle: (alert: Alert) => void; | ||
|
|
||
| /** | ||
| * Status for the alert whether it is enabled or disabled | ||
| */ | ||
| status?: boolean; | ||
| } | ||
|
|
||
| export const AlertInformationActionRow = ( | ||
| props: AlertInformationActionRowProps | ||
| ) => { | ||
| const { alert, handleToggle, status = false } = props; | ||
| const { id, label, rule_criteria, service_type, type } = alert; | ||
| const metricThreshold = processMetricCriteria(rule_criteria.rules); | ||
|
|
||
| return ( | ||
| <TableRow data-qa-alert-cell={id} data-testid={id} key={`alert-row-${id}`}> | ||
| <TableCell sx={{ width: 0 }}> | ||
| <FormControlLabel | ||
| control={ | ||
| <Toggle checked={status} onChange={() => handleToggle(alert)} /> | ||
| } | ||
| label={''} | ||
| /> | ||
| </TableCell> | ||
| <TableCell> | ||
| <Link to={`/monitor/alerts/definitions/detail/${service_type}/${id}`}> | ||
| {label} | ||
| </Link> | ||
| </TableCell> | ||
| <TableCell> | ||
| <MetricThreshold metricThreshold={metricThreshold} /> | ||
| </TableCell> | ||
| <TableCell> | ||
| <Typography | ||
| sx={(theme) => ({ | ||
| font: theme.tokens.typography.Label.Regular.S, | ||
| })} | ||
| > | ||
| {capitalize(type)} | ||
| </Typography> | ||
| </TableCell> | ||
| </TableRow> | ||
| ); | ||
| }; |
67 changes: 67 additions & 0 deletions
67
...anager/src/features/CloudPulse/Alerts/ContextualView/AlertInformationActionTable.test.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| import { within } from '@testing-library/react'; | ||
| import React from 'react'; | ||
|
|
||
| import { alertFactory } from 'src/factories/cloudpulse/alerts'; | ||
| import { renderWithTheme } from 'src/utilities/testHelpers'; | ||
|
|
||
| import { AlertInformationActionTable } from './AlertInformationActionTable'; | ||
|
|
||
| import type { | ||
| AlertInformationActionTableProps, | ||
| TableColumnHeader, | ||
| } from './AlertInformationActionTable'; | ||
|
|
||
| const serviceType = 'linode'; | ||
| const entityId = '123'; | ||
| const entityName = 'test-instance'; | ||
| const alerts = [ | ||
| ...alertFactory.buildList(7, { | ||
| entity_ids: [entityId], | ||
| service_type: serviceType, | ||
| status: 'enabled', | ||
| }), | ||
| ]; | ||
| const columns: TableColumnHeader[] = [ | ||
| { columnName: 'Alert Name', label: 'label' }, | ||
| { columnName: 'Metric Threshold', label: 'id' }, | ||
| { columnName: 'Alert Type', label: 'type' }, | ||
| ]; | ||
| const props: AlertInformationActionTableProps = { | ||
| alerts, | ||
| columns, | ||
| entityId, | ||
| entityName, | ||
| orderByColumn: 'Alert Name', | ||
| }; | ||
|
|
||
| describe('Alert Listing Reusable Table for contextual view', () => { | ||
| it('Should render alert table', () => { | ||
| const { getByText } = renderWithTheme( | ||
| <AlertInformationActionTable {...props} /> | ||
| ); | ||
|
|
||
| expect(getByText('Alert Name')).toBeInTheDocument(); | ||
| expect(getByText('Metric Threshold')).toBeInTheDocument(); | ||
| expect(getByText('Alert Type')).toBeInTheDocument(); | ||
| }); | ||
|
|
||
| it('Should show message for empty table', () => { | ||
| const { getByText } = renderWithTheme( | ||
| <AlertInformationActionTable {...props} alerts={[]} /> | ||
| ); | ||
|
|
||
| expect(getByText('No data to display.')).toBeInTheDocument(); | ||
| }); | ||
|
|
||
| it('Shoud render table row toggle in table row', async () => { | ||
| const { findByTestId } = renderWithTheme( | ||
| <AlertInformationActionTable {...props} /> | ||
| ); | ||
| const alert = alerts[0]; | ||
| const row = await findByTestId(alert.id); | ||
|
|
||
| const checkbox = await within(row).findByRole('checkbox'); | ||
|
|
||
| expect(checkbox).toHaveProperty('checked'); | ||
| }); | ||
| }); |
174 changes: 174 additions & 0 deletions
174
...ges/manager/src/features/CloudPulse/Alerts/ContextualView/AlertInformationActionTable.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,174 @@ | ||
| import { Box } from '@linode/ui'; | ||
| import { Grid, TableBody, TableHead } from '@mui/material'; | ||
| import React from 'react'; | ||
|
|
||
| import OrderBy from 'src/components/OrderBy'; | ||
| import Paginate from 'src/components/Paginate'; | ||
| import { PaginationFooter } from 'src/components/PaginationFooter/PaginationFooter'; | ||
| import { Table } from 'src/components/Table'; | ||
| import { TableCell } from 'src/components/TableCell'; | ||
| import { TableContentWrapper } from 'src/components/TableContentWrapper/TableContentWrapper'; | ||
| import { TableRow } from 'src/components/TableRow'; | ||
| import { TableSortCell } from 'src/components/TableSortCell'; | ||
| import { getAPIErrorOrDefault } from 'src/utilities/errorUtils'; | ||
|
|
||
| import { AlertInformationActionRow } from './AlertInformationActionRow'; | ||
|
|
||
| import type { APIError, Alert } from '@linode/api-v4'; | ||
|
|
||
| export interface AlertInformationActionTableProps { | ||
| /** | ||
| * List of alerts to be displayed | ||
| */ | ||
| alerts: Alert[]; | ||
|
|
||
| /** | ||
| * List of table headers for each column | ||
| */ | ||
| columns: TableColumnHeader[]; | ||
|
|
||
| /** | ||
| * Id of the selected entity | ||
| */ | ||
| entityId: string; | ||
|
|
||
| /** | ||
| * Name of the selected entity | ||
| */ | ||
| entityName: string; | ||
|
|
||
| /** | ||
| * Error received from API | ||
| */ | ||
| error?: APIError[] | null; | ||
|
|
||
| /** | ||
| * Column name by which columns will be ordered by default | ||
| */ | ||
| orderByColumn: string; | ||
| } | ||
|
|
||
| export interface TableColumnHeader { | ||
| /** | ||
| * Name of the column to be displayed | ||
| */ | ||
| columnName: string; | ||
|
|
||
| /** | ||
| * Corresponding key name in the alert object for which this column is | ||
| */ | ||
| label: string; | ||
| } | ||
|
|
||
| export interface AlertStatusMap { | ||
| /** | ||
| * Alert id to boolean mapping to hold whether that alert id is enabled or not | ||
| */ | ||
| [alertId: number]: boolean; | ||
| } | ||
|
|
||
| // generate the mapping of id to whether entity present in entity_ids list or not for quick access in table row toggles | ||
| const generateStatusMap = ( | ||
| alerts: Alert[], | ||
| entityId: string | ||
| ): AlertStatusMap => { | ||
| return alerts.reduce<AlertStatusMap>( | ||
| (previousValue, alert) => ({ | ||
| ...previousValue, | ||
| [alert.id]: alert.entity_ids.includes(entityId), | ||
| }), | ||
| {} | ||
| ); | ||
| }; | ||
|
|
||
| export const AlertInformationActionTable = ( | ||
| props: AlertInformationActionTableProps | ||
| ) => { | ||
| const { alerts, columns, entityId, error, orderByColumn } = props; | ||
| const [alertStatusMap, setAlertStatusMap] = React.useState<AlertStatusMap>( | ||
| generateStatusMap(alerts, entityId) | ||
| ); | ||
|
|
||
| const _error = error | ||
| ? getAPIErrorOrDefault(error, 'Error while fetching the alerts') | ||
| : undefined; | ||
|
|
||
| const handleToggle = ({ id }: Alert) => { | ||
| setAlertStatusMap((previousValue) => { | ||
| return { | ||
| ...previousValue, | ||
| [id]: !previousValue[id], | ||
| }; | ||
| }); | ||
| }; | ||
| return ( | ||
| <OrderBy data={alerts} order="asc" orderBy={orderByColumn}> | ||
| {({ data: orderedData, handleOrderChange, order, orderBy }) => ( | ||
| <Paginate data={orderedData}> | ||
| {({ | ||
| count, | ||
| data: paginatedAndOrderedAlerts, | ||
| handlePageChange, | ||
| handlePageSizeChange, | ||
| page, | ||
| pageSize, | ||
| }) => ( | ||
| <Box> | ||
| <Grid> | ||
| <Table | ||
| colCount={columns.length + 1} | ||
| data-testid="alert-table" | ||
| size="small" | ||
| > | ||
| <TableHead> | ||
| <TableRow> | ||
| <TableCell actionCell /> | ||
| {columns.map(({ columnName, label }) => { | ||
| return ( | ||
| <TableSortCell | ||
| active={orderBy === label} | ||
| data-qa-header={label} | ||
| data-qa-sorting={label} | ||
| direction={order} | ||
| handleClick={handleOrderChange} | ||
| key={label} | ||
| label={label} | ||
| > | ||
| {columnName} | ||
| </TableSortCell> | ||
| ); | ||
| })} | ||
| </TableRow> | ||
| </TableHead> | ||
| <TableBody> | ||
| <TableContentWrapper | ||
| error={_error} | ||
| length={paginatedAndOrderedAlerts.length} | ||
| loading={false} | ||
| /> | ||
| {paginatedAndOrderedAlerts?.map((alert) => ( | ||
| <AlertInformationActionRow | ||
| alert={alert} | ||
| handleToggle={handleToggle} | ||
| key={alert.id} | ||
| status={alertStatusMap[alert.id]} | ||
| /> | ||
| ))} | ||
| </TableBody> | ||
| </Table> | ||
| </Grid> | ||
| <PaginationFooter | ||
| count={count} | ||
| eventCategory="Alert Definitions Table" | ||
| handlePageChange={handlePageChange} | ||
| handleSizeChange={handlePageSizeChange} | ||
| page={page} | ||
| pageSize={pageSize} | ||
| /> | ||
| </Box> | ||
| )} | ||
| </Paginate> | ||
| )} | ||
| </OrderBy> | ||
| ); | ||
| }; | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 don't think we should do this after reviewing. The mapping is only used for the initial state of
alertStatusMap. After that, the component maintains its own state with handleToggle that updates this mapping without any connection to the original alert data.A better approach would be to add a new React Query mutation to synchronize the data and "optimistically" update the new value. We do this is several places in the app https://tanstack.com/query/latest/docs/framework/react/guides/optimistic-updates#optimistic-updates
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.
Sure, I'll add this approach in the next PR in which I'll be adding 2 react queries that are related to toggle button along with confirmation dialogue