Skip to content
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

OPG-480: Alarm Table Panel scrolling issue fix #1000

Draft
wants to merge 1 commit into
base: release-9.x
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
"latex",
"plaintext",
"asciidoc"
]
],
"editor.tabSize": 2
}
12,070 changes: 8,130 additions & 3,940 deletions package-lock.json

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"homepage": "https://github.com/OpenNMS/grafana-plugin",
"devDependencies": {
"@antora/cli": "^3.1.4",
"@antora/site-generator-default": "^3.1.4",
"@antora/site-generator-default": "^3.1.7",
"@babel/core": "^7.24.5",
"@grafana/e2e": "^9.5.5",
"@grafana/e2e-selectors": "^9.5.5",
Expand All @@ -30,15 +30,15 @@
"@types/glob": "^8.1.0",
"@types/grafana": "github:CorpGlory/types-grafana",
"@types/jest": "^29.5.12",
"@types/jquery": "^3.5.13",
"@types/node": "^18.17.0",
"@types/jquery": "^3.5.29",
"@types/node": "^18.19.32",
"@types/react-router-dom": "^5.3.3",
"commander": "^11.0.0",
"copy-webpack-plugin": "^12.0.2",
"css-loader": "^7.1.1",
"eslint-webpack-plugin": "^4.0.1",
"fork-ts-checker-webpack-plugin": "^9.0.0",
"fs-extra": "^11.1.0",
"fs-extra": "^11.2.0",
"glob": "^8.1.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^29.7.0",
Expand All @@ -48,12 +48,12 @@
"recursive-copy": "^2.0.13",
"replace-in-file-webpack-plugin": "^1.0.6",
"rimraf": "^5.0.0",
"sass": "^1.75.0",
"sass": "^1.76.0",
"sass-loader": "^14.2.1",
"specit": "^1.4.4",
"style-loader": "^4.0.0",
"swc-loader": "^0.2.6",
"ts-node": "^10.5.0",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.1.0",
"tslib": "^2.3.1",
"typescript": "^4.9.5",
Expand Down
44 changes: 24 additions & 20 deletions src/panels/alarm-table/AlarmTableControl.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React, { useEffect, useRef } from 'react'
import { AppEvents, PanelProps } from '@grafana/data'
import { AppEvents, DataFrame, PanelProps } from '@grafana/data'
import { getAppEvents } from '@grafana/runtime'
import { Button, ContextMenu, Modal, Pagination, Tab, TabContent, Table, TabsBar } from '@grafana/ui'
import { AlarmTableMenu } from './AlarmTableMenu'
import { AlarmTableModalContent } from './modal/AlarmTableModalContent'
import { AlarmTableSelectionStyles } from './AlarmTableSelectionStyles'
import { AlarmTableControlProps } from './AlarmTableTypes'
import { useAlarm } from './hooks/useAlarm'
import { useAlarmProperties } from './hooks/useAlarmProperties'
import { useAlarmTableMenuActions } from './hooks/useAlarmTableMenuActions'
import { useAlarmTableConfigDefaults } from './hooks/useAlarmTableConfigDefaults'
Expand All @@ -14,34 +14,36 @@ import { useAlarmTableRowHighlighter } from './hooks/useAlarmTableRowHighlighter
import { useAlarmTableSelection } from './hooks/useAlarmTableSelection'
import { useAlarmTableModalTabs } from './hooks/useAlarmTableModalTabs'
import { useOpenNMSClient } from '../../hooks/useOpenNMSClient'
import { useAlarm } from './hooks/useAlarm'
import { AlarmTableModalContent } from './modal/AlarmTableModalContent'
import { capitalize } from 'lib/utils'

export const AlarmTableControl: React.FC<PanelProps<AlarmTableControlProps>> = (props) => {
const alarmIndexes = useRef<boolean[]>([] as boolean[])

const { state, setState, rowClicked, soloIndex } = useAlarmTableSelection(() => {
setDetailsModal(true)
})
const selectedAlarmIds = useRef<Set<number>>(new Set<number>())
const filteredData = useRef<DataFrame>(props?.data?.series[0])
const table = useRef<HTMLDivElement>(null)

const { client } = useOpenNMSClient(props.data?.request?.targets?.[0]?.datasource)
const { filteredProps, page, setPage, totalPages } = useAlarmProperties(props?.data?.series[0], props?.options?.alarmTable)
const { table, menu, menuOpen, setMenuOpen } = useAlarmTableMenu(alarmIndexes, rowClicked, filteredProps, setState)
const { page, setPage, totalPages } = useAlarmProperties(filteredData, props?.data?.series[0], props?.options?.alarmTable)

const { alarmControlState, setAlarmControlState, rowClicked, soloAlarmId } = useAlarmTableSelection(() => { setDetailsModal(true) })

const { menu, menuOpen, setMenuOpen } = useAlarmTableMenu(table, alarmIndexes, selectedAlarmIds, rowClicked, filteredData, setAlarmControlState)

const { actions, detailsModal, setDetailsModal } = useAlarmTableMenuActions(
state.indexes,
props?.data?.series?.[0]?.fields || [],
alarmControlState.selectedIndexes,
alarmControlState.selectedAlarmIds,
() => setMenuOpen(false),
(actionName: string, results: any[]) => displayActionNotice(actionName, results),
props?.options?.alarmTable?.alarmTableAdditional?.useGrafanaUser ?? false,
client)

const { tabActive, tabClick, resetTabs } = useAlarmTableModalTabs()
const { alarm, goToAlarm, alarmQuery } = useAlarm(props?.data?.series, soloIndex, client)
const { alarm, goToAlarm, alarmQuery } = useAlarm(filteredData, soloAlarmId, client)

const paginationRef = useRef<HTMLDivElement>(null)

useAlarmTableRowHighlighter(state, table)
useAlarmTableRowHighlighter(alarmControlState, table, filteredData)
useAlarmTableConfigDefaults(props.fieldConfig, props.onFieldConfigChange, props.options)

/**
Expand Down Expand Up @@ -86,9 +88,10 @@ export const AlarmTableControl: React.FC<PanelProps<AlarmTableControlProps>> = (
}

useEffect(() => {
alarmIndexes.current = state.indexes
alarmIndexes.current = [...alarmControlState.selectedIndexes]
selectedAlarmIds.current = new Set(alarmControlState.selectedAlarmIds)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [state])
}, [alarmControlState])

return (
<div className={
Expand All @@ -106,18 +109,19 @@ export const AlarmTableControl: React.FC<PanelProps<AlarmTableControlProps>> = (
}>
<AlarmTableSelectionStyles />
<div className='alarm-table-wrapper'>
{alarmQuery ? <Table data={filteredProps} width={props.width} height={props.height - calcPaginationHeight()} /> :
<div>Select the Entity Datasource below, and choose an Alarm query to see results.</div>
{alarmQuery ?
<Table data={filteredData.current} width={props.width} height={props.height - calcPaginationHeight()} /> :
<div>Select the Entity Datasource below, and choose an Alarm query to see results.</div>
}
</div>
{menuOpen && <ContextMenu
x={menu.x}
y={menu.y}
onClose={() => {
resetTabs();
setMenuOpen(false);
resetTabs();
setMenuOpen(false);
}}
renderMenuItems={() => <AlarmTableMenu state={state} actions={actions} />}
renderMenuItems={() => <AlarmTableMenu state={alarmControlState} actions={actions} />}
/>}
<Modal isOpen={detailsModal} title='Alarm Detail' onDismiss={() => setDetailsModal(false)}>
<Button style={{ marginBottom: 12 }} onClick={goToAlarm}><i className='fa fa-external-link'></i>&nbsp;Full Details</Button>
Expand Down
92 changes: 47 additions & 45 deletions src/panels/alarm-table/AlarmTableData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,56 +6,58 @@ import { AlarmTableDataState } from './AlarmTableTypes'
import { alarmTableDefaultColumns } from './constants'

interface AlarmTableDataProps {
onChange: Function;
context: any;
onChange: Function
context: any
}

export const AlarmTableData: React.FC<AlarmTableDataProps> = ({ onChange, context }) => {
const [alarmTableData, setAlarmTableData] = useState<AlarmTableDataState>(context?.options?.alarmTable?.alarmTableData || {
columns: alarmTableDefaultColumns
})
const [alarmTableData, setAlarmTableData] = useState<AlarmTableDataState>(context?.options?.alarmTable?.alarmTableData || {
columns: alarmTableDefaultColumns
})

useEffect(() => {
onChange(alarmTableData);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [alarmTableData])
useEffect(() => {
onChange(alarmTableData)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [alarmTableData])

const setAlarmTableState = (key, value) => {
const newState = { ...alarmTableData }
newState[key] = value
setAlarmTableData(newState)
}
const setAlarmTableState = (key, value) => {
const newState = { ...alarmTableData }
newState[key] = value
setAlarmTableData(newState)
}

return (
<div>
{/**
*
* Commented out for now. This is in the original table, but
* from what I can tell there was only ever one transformer
* written, so there's no need to have the ability to swap them
* TODO: Double check if we need the ability to 'transform' in different ways
*
* <OnmsInlineField label="Table Transform">
<Select
value={alarmTableData.transformType}
onChange={(val) => setAlarmTableState('transformType', val)}
options={[{ label: 'Table', value: 0 }]}
/>
</OnmsInlineField> */}
return (
<div>
{
/**
*
* Commented out for now. This is in the original table, but
* from what I can tell there was only ever one transformer
* written, so there's no need to have the ability to swap them
* TODO: Double check if we need the ability to 'transform' in different ways
*
* <OnmsInlineField label="Table Transform">
<Select
value={alarmTableData.transformType}
onChange={(val) => setAlarmTableState('transformType', val)}
options={[{ label: 'Table', value: 0 }]}
/>
</OnmsInlineField> */
}

<OnmsInlineField label="Columns">
<Select
placeholder='Add Column'
value={''}
onChange={(val) => {
const newColumns = [...alarmTableData.columns]
newColumns.push(val)
setAlarmTableState('columns', newColumns)
}}
options={context?.data?.[0]?.fields.map((field, index) => ({ ...field, value: index, label: field.name }))}
/>
</OnmsInlineField>
<DragList values={alarmTableData?.columns} onChange={(val) => { setAlarmTableState('columns', val) }} />
</div>
)
<OnmsInlineField label="Columns">
<Select
placeholder='Add Column'
value={''}
onChange={(val) => {
const newColumns = [...alarmTableData.columns]
newColumns.push(val)
setAlarmTableState('columns', newColumns)
}}
options={context?.data?.[0]?.fields.map((field, index) => ({ ...field, value: index, label: field.name }))}
/>
</OnmsInlineField>
<DragList values={alarmTableData?.columns} onChange={(val) => { setAlarmTableState('columns', val) }} />
</div>
)
}
46 changes: 44 additions & 2 deletions src/panels/alarm-table/AlarmTableHelper.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,47 @@
import { Field } from '@grafana/data'
import { DataFrame, Field } from '@grafana/data'

export const getAlarmIdFromFields = (fields: Field[], index: number) => {
return fields.find((d) => d.name === 'ID')?.values.get(index)
return fields.find((d) => d.name === 'ID')?.values.get(index)
}

// given an array of row elements (divs in the Alarm Table), return the alarmIds
// associated with the rows, in row order
// rows can be generated via something like: table.current?.querySelectorAll('.table-body div[role="row"]')
export const getAlarmIdsForRows = (rows: Element[], frame: DataFrame) => {
return rows.map(row => getAlarmIdFromRow(row, frame))
}

/**
* Find the current 0-based column index of the Alarm ID field.
* This should be the frame after column including/exclusion and column sorting have been applied.
*/
export const getColumnIndexOfAlarmId = (frame: DataFrame) => {
return frame.fields.findIndex(f => f.name === 'ID')
}

/**
* Get the Alarm ID from the table cell HTMLElement.
*/
export const getAlarmIdFromRow = (row: Element, frame: DataFrame) => {
// const row = cell.parentElement
const columnIndex = getColumnIndexOfAlarmId(frame)

if (row && columnIndex >= 0) {
const dataIndexCell = row?.childNodes?.[columnIndex] || null
const text = dataIndexCell?.textContent

const alarmId = Number(text)
return Number.isInteger(alarmId) ? alarmId : -1
}

return -1
}

/**
* Get the Alarm ID from the table cell Element.
*/
export const getAlarmIdFromCell = (cell: Element, frame: DataFrame) => {
const row = cell.parentElement

return row ? getAlarmIdFromRow(row, frame) : -1
}
4 changes: 2 additions & 2 deletions src/panels/alarm-table/AlarmTableMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ interface AlarmTableMenuProps {
}

export const AlarmTableMenu: React.FC<AlarmTableMenuProps> = ({ state,actions }) => {
const selectedCount = state.indexes.filter(d => d === true).length
const selectedCount = state.selectedAlarmIds.size
const suffix = selectedCount > 1 ? ` (${selectedCount})` : ''

let items = [
Expand Down Expand Up @@ -42,7 +42,7 @@ export const AlarmTableMenu: React.FC<AlarmTableMenuProps> = ({ state,actions })
return (
<Menu>
{items.map((item, index) => {
let elem = <MenuItem label={item.label} key={index} onClick={item.action} />
let elem = <MenuItem label={item.label} key={item.label} onClick={item.action} />

if (item.type === 'divider') {
elem = <div className={styles.divider}></div>
Expand Down
20 changes: 12 additions & 8 deletions src/panels/alarm-table/AlarmTableTypes.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { SelectableValue } from '@grafana/data'

export interface AlarmTableControlState {
indexes: boolean[]
selectedAlarmIds: Set<number>
selectedIndexes: boolean[]
lastClicked: number
lastClickedAlarmId: number
}

export interface AlarmTableAdditionalState {
Expand Down Expand Up @@ -37,14 +39,16 @@ export interface AlarmTableColumnSizeState {
columnSizes: AlarmTableColumnSizeItem[]
}

export interface AlarmTableOptionsState {
alarmTableAdditional: AlarmTableAdditionalState
alarmTableAlarms: AlarmTableAlarmDataState
alarmTableData: AlarmTableDataState
alarmTablePaging: AlarmTablePaginationState
alarmTableColumnSizes?: AlarmTableColumnSizeState
}

export interface AlarmTableControlProps {
alarmTable: {
alarmTableAdditional: AlarmTableAdditionalState
alarmTableAlarms: AlarmTableAlarmDataState
alarmTableData: AlarmTableAlarmDataState
alarmTablePaging: AlarmTablePaginationState
alarmTableColumnSizes?: AlarmTableColumnSizeState
}
alarmTable: AlarmTableOptionsState
}

export interface AlarmTableControlActions {
Expand Down
1 change: 1 addition & 0 deletions src/panels/alarm-table/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ export const alarmTableDefaultColumns = [
{ label: 'Location', value: 8 },
{ label: 'Node Label', value: 14 },
{ label: 'Log Message', value: 9 },
{ label: 'ID', value: 0 },
]
Loading
Loading