Skip to content
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.22.3",
"version": "1.22.4",
"description": "Supporting common component library",
"type": "module",
"main": "dist/index.js",
Expand Down
4 changes: 3 additions & 1 deletion src/Common/Drawer/Drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { VisibleModal } from '../Modals/VisibleModal'
import './Drawer.scss'
import { DTFocusTrapType } from '@Shared/Components/DTFocusTrap'

export interface DrawerProps extends Pick<DTFocusTrapType, 'initialFocus'> {
export interface DrawerProps extends Pick<DTFocusTrapType, 'initialFocus' | 'avoidFocusTrap' > {
position: 'left' | 'right' | 'bottom' | 'top'
children?: any
backdrop?: boolean
Expand All @@ -45,6 +45,7 @@ export const Drawer = ({
onClose,
disableTransition,
initialFocus = undefined,
avoidFocusTrap = false,
}: DrawerProps) => {
const drawerRef = useRef(null)
useEffect(() => {
Expand All @@ -69,6 +70,7 @@ export const Drawer = ({
onEscape={onEscape}
close={onClose}
initialFocus={initialFocus}
avoidFocusTrap={avoidFocusTrap}
>
<aside
style={style}
Expand Down
2 changes: 2 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<{
close?: (e?) => void
onEscape?: (e?) => void
initialFocus?: DTFocusTrapType['initialFocus']
avoidFocusTrap?: boolean
}> {
constructor(props) {
super(props)
Expand Down Expand Up @@ -56,6 +57,7 @@ export class VisibleModal extends React.Component<{
onEscape={this.escFunction}
onClick={this.handleBodyClick}
initialFocus={this.props.initialFocus ?? undefined}
avoidFocusTrap={this.props.avoidFocusTrap}
>
<div className={this.props.parentClassName}>
<div className={`visible-modal__body ${this.props.className || ''}`}>{this.props.children}</div>
Expand Down
2 changes: 2 additions & 0 deletions src/Shared/Components/Backdrop/Backdrop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const Backdrop = ({
deactivateFocusOnEscape = true,
initialFocus,
returnFocusOnDeactivate,
avoidFocusTrap,
}: BackdropProps) => {
// STATES
const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(null)
Expand Down Expand Up @@ -92,6 +93,7 @@ const Backdrop = ({
deactivateFocusOnEscape={deactivateFocusOnEscape}
initialFocus={initialFocus ?? undefined}
returnFocusOnDeactivate={returnFocusOnDeactivate}
avoidFocusTrap={avoidFocusTrap}
>
<motion.div
initial={{ opacity: 0 }}
Expand Down
5 changes: 4 additions & 1 deletion src/Shared/Components/Backdrop/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ import { MouseEvent, ReactNode } from 'react'
import { DTFocusTrapType } from '../DTFocusTrap'

export interface BackdropProps
extends Pick<DTFocusTrapType, 'deactivateFocusOnEscape' | 'initialFocus' | 'onEscape' | 'returnFocusOnDeactivate'> {
extends Pick<
DTFocusTrapType,
'deactivateFocusOnEscape' | 'initialFocus' | 'onEscape' | 'returnFocusOnDeactivate' | 'avoidFocusTrap'
> {
/**
* The content to be rendered within the backdrop component.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ const ConfirmationModalBody = ({
shouldCloseOnEscape = true,
isLandscapeView = false,
showConfetti = false,
avoidFocusTrap = false,
}: ConfirmationModalBodyProps) => {
const { registerShortcut, unregisterShortcut } = useRegisterShortcut()

Expand Down Expand Up @@ -92,6 +93,7 @@ const ConfirmationModalBody = ({
deactivateFocusOnEscape={shouldCloseOnEscape}
// Since when custom input is present, we auto focus on input, else focus on primary button
initialFocus={confirmationConfig ? false : `#${PRIMARY_BUTTON_ID}`}
avoidFocusTrap={avoidFocusTrap}
>
<motion.div
className={`${isLandscapeView ? 'w-500' : 'w-400'} confirmation-modal border__secondary flexbox-col br-8 bg__primary dc__m-auto mt-40`}
Expand Down
1 change: 1 addition & 0 deletions src/Shared/Components/ConfirmationModal/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ export type ConfirmationModalProps<isConfig extends boolean = false> = PropsWith
* @default false
*/
showConfetti?: boolean
avoidFocusTrap?: boolean
}> &
ButtonConfigAndVariantType<isConfig> &
(isConfig extends false
Expand Down
76 changes: 24 additions & 52 deletions src/Shared/Components/DTFocusTrap/DTFocusTrap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,40 +14,22 @@
* limitations under the License.
*/

import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'
import { useCallback, useEffect } from 'react'
import FocusTrap from 'focus-trap-react'

import { noop } from '@Common/Helper'
import { ALLOW_ACTION_OUTSIDE_FOCUS_TRAP } from '@Shared/constants'
import { preventBodyScroll } from '@Shared/Helpers'

import { DTFocusTrapType } from './types'

const FocusTrapControlContext = createContext<{
disableFocusTrap: () => void
resumeFocusTrap: () => void
}>(null)

export const useFocusTrapControl = () => {
const context = useContext(FocusTrapControlContext)
if (!context) {
return {
disableFocusTrap: noop,
resumeFocusTrap: noop,
}
}
return context
}

const DTFocusTrap = ({
onEscape,
deactivateFocusOnEscape = true,
children,
initialFocus = undefined,
returnFocusOnDeactivate = true,
avoidFocusTrap = false,
}: DTFocusTrapType) => {
const [isFocusEnabled, setIsFocusEnabled] = useState(true)

const handleEscape = useCallback(
(e?: KeyboardEvent | MouseEvent) => {
onEscape(e)
Expand All @@ -64,40 +46,30 @@ const DTFocusTrap = ({
}
}, [])

const focusContextValue = useMemo(
() => ({
disableFocusTrap: () => setIsFocusEnabled(false),
resumeFocusTrap: () => setIsFocusEnabled(true),
}),
[],
)

return (
<FocusTrapControlContext.Provider value={focusContextValue}>
<FocusTrap
active={isFocusEnabled}
focusTrapOptions={{
escapeDeactivates: handleEscape,
initialFocus,
allowOutsideClick: (event) => {
// Allow up to 3 parent levels to check for the allowed class
let el = event.target as Element | null
let depth = 0
while (el && depth < 4) {
if (el.classList && el.classList.contains(ALLOW_ACTION_OUTSIDE_FOCUS_TRAP)) {
return true
}
el = el.parentElement
depth += 1
<FocusTrap
active={!avoidFocusTrap}
focusTrapOptions={{
escapeDeactivates: handleEscape,
initialFocus,
allowOutsideClick: (event) => {
// Allow up to 3 parent levels to check for the allowed class
let el = event.target as Element | null
let depth = 0
while (el && depth < 4) {
if (el.classList && el.classList.contains(ALLOW_ACTION_OUTSIDE_FOCUS_TRAP)) {
return true
}
return false
},
returnFocusOnDeactivate,
}}
>
{children}
</FocusTrap>
</FocusTrapControlContext.Provider>
el = el.parentElement
depth += 1
}
return false
},
returnFocusOnDeactivate,
}}
>
{children}
</FocusTrap>
)
}

Expand Down
2 changes: 1 addition & 1 deletion src/Shared/Components/DTFocusTrap/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@
* limitations under the License.
*/

export { default as DTFocusTrap, useFocusTrapControl } from './DTFocusTrap'
export { default as DTFocusTrap } from './DTFocusTrap'
export type { DTFocusTrapType } from './types'
1 change: 1 addition & 0 deletions src/Shared/Components/DTFocusTrap/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,5 @@ export interface DTFocusTrapType extends Pick<FocusTrapProps['focusTrapOptions']
* Setting this option to undefined (or to a function that returns undefined) will result in the first element in the focus trap's tab order receiving focus.
*/
initialFocus?: FocusTargetOrFalse | undefined | (() => void)
avoidFocusTrap?: boolean
}
13 changes: 1 addition & 12 deletions src/Shared/Components/DatePicker/DateTimePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import { useEffect, useState } from 'react'
import { useState } from 'react'
import { SingleDatePicker } from 'react-dates'
import CustomizableCalendarDay from 'react-dates/esm/components/CustomizableCalendarDay'
import { SelectInstance } from 'react-select'
Expand All @@ -28,7 +28,6 @@ import { ComponentSizeType } from '@Shared/constants'
import 'react-dates/initialize'

import { DATE_TIME_FORMATS } from '../../../Common'
import { useFocusTrapControl } from '../DTFocusTrap'
import { SelectPicker } from '../SelectPicker'
import { customDayStyles, DATE_PICKER_IDS, DATE_PICKER_PLACEHOLDER } from './constants'
import { DateTimePickerProps } from './types'
Expand Down Expand Up @@ -57,8 +56,6 @@ const DateTimePicker = ({
const selectedTimeOption = DEFAULT_TIME_OPTIONS.find((option) => option.value === time) ?? DEFAULT_TIME_OPTIONS[0]
const [isFocused, setFocused] = useState(false)

const { disableFocusTrap, resumeFocusTrap } = useFocusTrapControl()

const handleFocusChange = ({ focused }) => {
setFocused(focused)
}
Expand All @@ -74,14 +71,6 @@ const DateTimePicker = ({
// Function to disable dates including today and all past dates
const isDayBlocked = (day) => isTodayBlocked && !day.isAfter(today)

useEffect(() => {
if (isFocused) {
disableFocusTrap()
return
}
resumeFocusTrap()
}, [isFocused])

return (
<div className="date-time-picker">
{label && (
Expand Down
Loading