Skip to content

fix: rb table #804

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
merged 10 commits into from
Jul 2, 2025
Merged
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
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@devtron-labs/devtron-fe-common-lib",
"version": "1.16.0-pre-4",
"version": "1.16.0-beta-6",
"description": "Supporting common component library",
"type": "module",
"main": "dist/index.js",
Expand Down
66 changes: 32 additions & 34 deletions src/Common/Checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
* limitations under the License.
*/

import { forwardRef } from 'react'

import { stopPropagation } from '@Common/Helper'

import { CheckboxProps } from '../Types'

import './Checkbox.scss'
Expand All @@ -28,38 +32,32 @@ Valid States of Checkbox:
6. disabled: false, checked: true, value: CHECKED
*/
// TODO: Associate label with input element
export const Checkbox = ({
rootClassName,
onClick,
name,
disabled,
value,
onChange,
tabIndex,
isChecked,
id,
dataTestId,
children,
}: CheckboxProps) => {
const rootClass = `${rootClassName || ''}`
export const Checkbox = forwardRef<HTMLLabelElement, CheckboxProps>(
(
{ rootClassName, onClick, name, disabled, value, onChange, tabIndex, isChecked, id, dataTestId, children },
forwardedRef,
) => {
const rootClass = `${rootClassName || ''}`

return (
// eslint-disable-next-line jsx-a11y/label-has-associated-control
<label className={`dc__position-rel flex left cursor ${rootClass}`} onClick={onClick}>
<input
{...(name ? { name } : {})}
type="checkbox"
className="form__checkbox"
disabled={disabled}
value={value}
onChange={onChange}
tabIndex={tabIndex}
checked={isChecked}
id={id}
data-testid={dataTestId}
/>
<span className="form__checkbox-container" data-testid={`${dataTestId}-chk-span`} />
<span className="form__checkbox-label">{children}</span>
</label>
)
}
return (
// eslint-disable-next-line jsx-a11y/label-has-associated-control
<label ref={forwardedRef} className={`dc__position-rel flex left cursor ${rootClass}`} onClick={onClick}>
<input
{...(name ? { name } : {})}
type="checkbox"
className="form__checkbox"
disabled={disabled}
value={value}
onChange={onChange}
tabIndex={tabIndex}
checked={isChecked}
id={id}
data-testid={dataTestId}
onClick={stopPropagation}
/>
<span className="form__checkbox-container" data-testid={`${dataTestId}-chk-span`} />
<span className="form__checkbox-label">{children}</span>
</label>
)
},
)
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
* limitations under the License.
*/

import { useCallback, useEffect, useMemo, useRef } from 'react'
import { KeyboardEvent, useCallback, useEffect, useMemo, useRef } from 'react'
import { deepEquals } from '@rjsf/utils'

import { noop } from '@Common/Helper'

import { ShortcutType, UseRegisterShortcutContextType, UseRegisterShortcutProviderType } from './types'
import { UseRegisterShortcutContext } from './UseRegisterShortcutContext'
import { preprocessKeys, verifyCallbackStack } from './utils'
Expand All @@ -25,7 +27,7 @@ const IGNORE_TAGS_FALLBACK = ['input', 'textarea', 'select']
const DEFAULT_TIMEOUT = 300

const UseRegisterShortcutProvider = ({
containerRef,
shouldHookOntoWindow = true,
ignoreTags,
preventDefault = false,
shortcutTimeout,
Expand Down Expand Up @@ -117,13 +119,12 @@ const UseRegisterShortcutProvider = ({
}
}, [])

const handleKeydownEvent = useCallback((event: KeyboardEvent) => {
const handleKeydownEvent = useCallback((event: KeyboardEvent<HTMLElement>) => {
if (
// NOTE: in case of custom events generated by password managers autofill, the event.key is not set
!event.key ||
(ignoredTags.map((tag) => tag.toUpperCase()).indexOf((event.target as HTMLElement).tagName?.toUpperCase()) >
-1 &&
(!containerRef || containerRef.current?.contains(event.target as HTMLElement))) ||
ignoredTags.map((tag) => tag.toUpperCase()).indexOf((event.target as HTMLElement).tagName?.toUpperCase()) >
-1 ||
(event.target as HTMLElement)?.role === 'textbox' ||
disableShortcutsRef.current
) {
Expand Down Expand Up @@ -161,29 +162,48 @@ const UseRegisterShortcutProvider = ({
}, [])

useEffect(() => {
window.addEventListener('keydown', handleKeydownEvent)
if (!shouldHookOntoWindow) {
return noop
}

window.addEventListener('keydown', handleKeydownEvent as unknown as (event: globalThis.KeyboardEvent) => void)
window.addEventListener('keyup', handleKeyupEvent)
window.addEventListener('blur', handleBlur)

return () => {
window.removeEventListener('keydown', handleKeydownEvent)
window.removeEventListener(
'keydown',
handleKeydownEvent as unknown as (event: globalThis.KeyboardEvent) => void,
)
window.removeEventListener('keyup', handleKeyupEvent)
window.removeEventListener('blur', handleBlur)

if (keyDownTimeoutRef.current > -1) {
clearTimeout(keyDownTimeoutRef.current)
}
}
}, [handleKeyupEvent, handleKeydownEvent, handleBlur])
}, [handleKeyupEvent, handleKeydownEvent, handleBlur, shouldHookOntoWindow])

const providerValue: UseRegisterShortcutContextType = useMemo(
() => ({
registerShortcut,
unregisterShortcut,
setDisableShortcuts,
triggerShortcut,
...(!shouldHookOntoWindow
? { targetProps: { onKeyDown: handleKeydownEvent, onKeyUp: handleKeyupEvent, onBlur: handleBlur } }
: {}),
}),
[registerShortcut, unregisterShortcut, setDisableShortcuts, triggerShortcut],
[
registerShortcut,
unregisterShortcut,
setDisableShortcuts,
triggerShortcut,
shouldHookOntoWindow,
handleKeydownEvent,
handleKeyupEvent,
handleBlur,
],
)

return <UseRegisterShortcutContext.Provider value={providerValue}>{children}</UseRegisterShortcutContext.Provider>
Expand Down
20 changes: 18 additions & 2 deletions src/Common/Hooks/UseRegisterShortcut/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import { RefObject } from 'react'
import { FocusEvent, KeyboardEvent } from 'react'

import { IS_PLATFORM_MAC_OS } from '@Common/Constants'

Expand Down Expand Up @@ -80,10 +80,26 @@ export interface UseRegisterShortcutContextType {
* Programmatically trigger a shortcut if already registered
*/
triggerShortcut: (keys: ShortcutType['keys']) => void
/**
* If shouldHookOntoWindow is false, these props need to be hooked onto
* the component that needs to listen to the shortcuts
*/
targetProps?: {
onKeyDown: (event: KeyboardEvent<HTMLElement>) => void
onKeyUp: (event: KeyboardEvent<HTMLElement>) => void
onBlur: (event: FocusEvent<HTMLElement>) => void
}
}

export interface UseRegisterShortcutProviderType {
containerRef?: RefObject<HTMLElement>
/**
* If false, the shortcuts will not be registered to the window object
* instead onKeyDown, onKeyUp and onBlur will be exposed as context methods
* which need to be hooked onto the component that needs to listen to the shortcuts
*
* defaults to true
*/
shouldHookOntoWindow?: boolean
children: React.ReactNode
/**
* Defines how long after holding the keys down do we trigger the callback in milliseconds
Expand Down
5 changes: 5 additions & 0 deletions src/Common/Modals/VisibleModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export class VisibleModal extends React.Component<{
onEscape?: (e) => void
}> {
modalRef = document.getElementById('visible-modal')
previousActiveElement: HTMLElement | null = null

constructor(props) {
super(props)
Expand All @@ -50,6 +51,8 @@ export class VisibleModal extends React.Component<{
this.modalRef.classList.add(this.props.noBackground ? 'show' : 'show-with-bg')
preventBodyScroll(true)

this.previousActiveElement = document.activeElement as HTMLElement

if (this.props.parentClassName) {
this.modalRef.classList.add(this.props.parentClassName)
}
Expand All @@ -61,6 +64,8 @@ export class VisibleModal extends React.Component<{
this.modalRef.classList.remove('show-with-bg')
preventBodyScroll(false)

this.previousActiveElement?.focus({ preventScroll: true })

if (this.props.parentClassName) {
this.modalRef.classList.remove(this.props.parentClassName)
}
Expand Down
3 changes: 3 additions & 0 deletions src/Common/Modals/VisibleModal2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { stopPropagation } from '../Helper'

export class VisibleModal2 extends React.Component<{ className?: string; close?: (e) => void }> {
modalRef = document.getElementById('visible-modal-2')
previousActiveElement: HTMLElement | null = null

constructor(props) {
super(props)
Expand All @@ -38,12 +39,14 @@ export class VisibleModal2 extends React.Component<{ className?: string; close?:
document.addEventListener('keydown', this.escFunction)
this.modalRef.classList.add('show-with-bg')
preventBodyScroll(true)
this.previousActiveElement = document.activeElement as HTMLElement
}

componentWillUnmount() {
document.removeEventListener('keydown', this.escFunction)
this.modalRef.classList.remove('show-with-bg')
preventBodyScroll(false)
this.previousActiveElement?.focus({ preventScroll: true })
}

handleBodyClick = (e: SyntheticEvent) => {
Expand Down
16 changes: 16 additions & 0 deletions src/Pages/ResourceBrowser/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import { ReactComponent as ICCleanBrush } from '@Icons/ic-medium-clean-brush.svg'
import { ReactComponent as ICMediumPause } from '@Icons/ic-medium-pause.svg'
import { ReactComponent as ICMediumPlay } from '@Icons/ic-medium-play.svg'
import { URLS } from '@Common/Constants'
import { SelectPickerOptionType } from '@Shared/Components'

import { NodeDrainRequest } from './types'
Expand Down Expand Up @@ -114,3 +115,18 @@ export const NODE_DRAIN_OPTIONS_CHECKBOX_CONFIG: {

export const GVK_FILTER_KIND_QUERY_PARAM_KEY = 'gvkFilterKind'
export const GVK_FILTER_API_VERSION_QUERY_PARAM_KEY = 'gvkFilterApiVersion'

export const DUMMY_RESOURCE_GVK_VERSION = 'v1'

export const RESOURCE_BROWSER_ROUTES = {
OVERVIEW: `${URLS.RESOURCE_BROWSER}/:clusterId/overview`,
MONITORING_DASHBOARD: `${URLS.RESOURCE_BROWSER}/:clusterId/monitoring-dashboard`,
TERMINAL: `${URLS.RESOURCE_BROWSER}/:clusterId/terminal`,
CLUSTER_UPGRADE: `${URLS.RESOURCE_BROWSER}/:clusterId/cluster-upgrade`,
NODE_DETAIL: `${URLS.RESOURCE_BROWSER}/:clusterId/node/:name`,
K8S_RESOURCE_DETAIL: `${URLS.RESOURCE_BROWSER}/:clusterId/:namespace/:kind/:group/:version/:name`,
K8S_RESOURCE_LIST: `${URLS.RESOURCE_BROWSER}/:clusterId/:kind/:group/:version`,
RESOURCE_RECOMMENDER: `${URLS.RESOURCE_BROWSER}/:clusterId/resource-recommender`,
} as const

export const K8S_EMPTY_GROUP = 'k8sEmptyGroup'
19 changes: 19 additions & 0 deletions src/Pages/ResourceBrowser/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,22 @@ export interface InstallationClusterConfigType
status: InstallationClusterStatus
correspondingClusterId: number | 0
}

export enum NodeActionMenuOptionIdEnum {
terminal = 'terminal',
cordon = 'cordon',
uncordon = 'uncordon',
drain = 'drain',
editTaints = 'edit-taints',
editYaml = 'edit-yaml',
delete = 'delete',
}

export enum ResourceBrowserActionMenuEnum {
manifest = 'manifest',
events = 'events',
logs = 'logs',
terminal = 'terminal',
delete = 'delete',
vulnerability = 'vulnerability',
}
4 changes: 1 addition & 3 deletions src/Shared/Components/ActionMenu/useActionMenu.hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,10 @@ export const useActionMenu = <T extends string | number>({
}
}

const handleTriggerKeyDown: UsePopoverProps['onTriggerKeyDown'] = (e, openState, closePopover) => {
const handleTriggerKeyDown: UsePopoverProps['onTriggerKeyDown'] = (e, openState) => {
if (!openState && (e.key === 'Enter' || e.key === ' ')) {
setFocusedIndex(0)
}

handlePopoverKeyDown(e, openState, closePopover)
}

// POPOVER HOOK
Expand Down
4 changes: 4 additions & 0 deletions src/Shared/Components/Backdrop/Backdrop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ const Backdrop = ({ children, onEscape, onClick, hasClearBackground = false, onB
}, [onEscape])

useEffect(() => {
const previousActiveElement = document.activeElement as HTMLElement

preventBodyScroll(true)
// Setting main as inert to that focus is trapped inside the new portal
preventOutsideFocus({ identifier: DEVTRON_BASE_MAIN_ID, preventFocus: true })
Expand All @@ -56,6 +58,8 @@ const Backdrop = ({ children, onEscape, onClick, hasClearBackground = false, onB
preventOutsideFocus({ identifier: DEVTRON_BASE_MAIN_ID, preventFocus: false })
preventOutsideFocus({ identifier: 'visible-modal', preventFocus: false })
preventOutsideFocus({ identifier: 'visible-modal-2', preventFocus: false })

previousActiveElement?.focus({ preventScroll: true })
}
}, [])

Expand Down
Loading