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 4 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-5",
"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,12 +162,19 @@ 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)

Expand All @@ -182,6 +190,9 @@ const UseRegisterShortcutProvider = ({
unregisterShortcut,
setDisableShortcuts,
triggerShortcut,
...(!shouldHookOntoWindow
? { targetProps: { onKeyDown: handleKeydownEvent, onKeyUp: handleKeyupEvent, onBlur: handleBlur } }
: {}),
}),
[registerShortcut, unregisterShortcut, setDisableShortcuts, triggerShortcut],
)
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
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
32 changes: 12 additions & 20 deletions src/Shared/Components/BulkSelection/BulkSelection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { useBulkSelection } from './BulkSelectionProvider'
import { BULK_DROPDOWN_TEST_ID, BulkSelectionOptionsLabels } from './constants'
import { BulkSelectionEvents, BulkSelectionProps } from './types'

const BulkSelection = forwardRef<HTMLButtonElement, BulkSelectionProps>(
const BulkSelection = forwardRef<HTMLLabelElement, BulkSelectionProps>(
(
{ showPagination, disabled = false, showChevronDownIcon = true, selectAllIfNotPaginated = false },
forwardedRef,
Expand Down Expand Up @@ -84,27 +84,19 @@ const BulkSelection = forwardRef<HTMLButtonElement, BulkSelectionProps>(
},
]}
>
<div className="dc__position-rel flexbox">
<div className="flexbox dc__gap-4 dc__align-items-center">
<Checkbox
isChecked={isChecked}
onChange={noop}
rootClassName="icon-dim-20 m-0"
value={checkboxValue}
disabled={disabled}
// Ideally should be disabled but was giving issue with cursor
/>

{showChevronDownIcon && <ICChevronDown className="icon-dim-20 fcn-6 dc__no-shrink" />}
</div>

<button
<div className="flexbox dc__gap-4 dc__align-items-center">
<Checkbox
ref={forwardedRef}
type="button"
className="dc__position-abs dc__left-0 dc__top-0 h-100 w-100 dc__zi-1 p-0 dc__no-border dc__outline-none dc__transparent--unstyled"
aria-label="Bulk selection dropdown"
onClick={selectAllIfNotPaginated && !showPagination ? onSinglePageSelectAll : noop}
isChecked={isChecked}
onChange={noop}
rootClassName="icon-dim-20 m-0"
value={checkboxValue}
disabled={disabled}
onClick={selectAllIfNotPaginated && !showPagination ? onSinglePageSelectAll : null}
// Ideally should be disabled but was giving issue with cursor
/>

{showChevronDownIcon && <ICChevronDown className="icon-dim-20 fcn-6 dc__no-shrink" />}
</div>
</ActionMenu>
)
Expand Down
2 changes: 1 addition & 1 deletion src/Shared/Components/CICDHistory/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ export const getHistoryItemStatusIconFromWorkflowStages = (
}

export const getWorkerPodBaseUrl = (clusterId: number = DEFAULT_CLUSTER_ID, podNamespace: string = DEFAULT_NAMESPACE) =>
`/resource-browser/${clusterId}/${podNamespace}/pod/k8sEmptyGroup`
`/resource-browser/${clusterId}/${podNamespace}/pod/k8sEmptyGroup/v1`

export const getWorkflowNodeStatusTitle = (status: string) => {
if (!status) {
Expand Down
5 changes: 2 additions & 3 deletions src/Shared/Components/Popover/usePopover.hook.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MouseEvent, useLayoutEffect, useRef, useState } from 'react'
import { useLayoutEffect, useRef, useState } from 'react'

import { UsePopoverProps, UsePopoverReturnType } from './types'
import {
Expand Down Expand Up @@ -39,8 +39,7 @@ export const usePopover = ({
onOpen?.(openState)
}

const togglePopover = (e: MouseEvent<HTMLDivElement>) => {
e.stopPropagation()
const togglePopover = () => {
updateOpenState(!open)
}

Expand Down
13 changes: 1 addition & 12 deletions src/Shared/Components/Table/BulkSelectionActionWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@
* Copyright (c) 2024. Devtron Inc.
*/

import { MouseEvent, useEffect } from 'react'
import { MouseEvent } from 'react'

import { ReactComponent as ICClose } from '@Icons/ic-close.svg'
import { DraggableButton, DraggablePositionVariant, DraggableWrapper } from '@Common/DraggableWrapper'
import { useRegisterShortcut } from '@Common/Hooks'
import { ComponentSizeType } from '@Shared/constants'

import { Button, ButtonComponentType, ButtonStyleType, ButtonVariantType } from '../Button'
Expand All @@ -21,16 +20,6 @@ const BulkSelectionActionWidget = ({
bulkActionsData,
setBulkActionState,
}: BulkSelectionActionWidgetProps) => {
const { registerShortcut, unregisterShortcut } = useRegisterShortcut()

useEffect(() => {
registerShortcut({ keys: ['Escape'], callback: handleClearBulkSelection })

return () => {
unregisterShortcut(['Escape'])
}
}, [])

const onActionClick = (event: MouseEvent<HTMLButtonElement>) => {
const {
dataset: { key },
Expand Down
Loading