Skip to content

Commit

Permalink
refactor: modal and search component fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
d-rita committed Feb 4, 2025
1 parent 801d3c7 commit d3114bb
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 145 deletions.
67 changes: 13 additions & 54 deletions components/header-bar/src/command-palette/command-palette.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import { colors, spacers } from '@dhis2/ui-constants'
import { IconApps24 } from '@dhis2/ui-icons'
import PropTypes from 'prop-types'
import React, { useCallback, useRef, useEffect } from 'react'
import i18n from '../locales/index.js'
import { useCommandPaletteContext } from './context/command-palette-context.js'
import { useAvailableActions } from './hooks/use-actions.js'
import { useFilter } from './hooks/use-filter.js'
import { useNavigation } from './hooks/use-navigation.js'
import useViewAndSectionHandler from './hooks/use-view-handler.js'
import BackButton from './sections/back-button.js'
import ModalContainer from './sections/container.js'
import Search from './sections/search-field.js'
import ModalContainer from './sections/modal-container.js'
import SearchFilter from './sections/search-field.js'
import {
ALL_APPS_VIEW,
ALL_COMMANDS_VIEW,
Expand All @@ -25,12 +25,7 @@ import {

const CommandPalette = ({ apps, commands, shortcuts }) => {
const containerEl = useRef(null)
const { currentView, filter, setFilter } = useCommandPaletteContext()

const handleFilterChange = useCallback(
({ value }) => setFilter(value),
[setFilter]
)
const { currentView, filter, setShowGrid } = useCommandPaletteContext()

const actionsArray = useAvailableActions({ apps, shortcuts, commands })

Expand All @@ -41,42 +36,22 @@ const CommandPalette = ({ apps, commands, shortcuts }) => {
currentViewItemsArray,
} = useFilter({ apps, commands, shortcuts })

const {
handleKeyDown,
goToDefaultView,
modalRef,
setModalOpen,
showModal,
} = useNavigation({
useEffect(() => {
setShowGrid(apps?.length > 0)
}, [apps, setShowGrid])

const { handleKeyDown, modalRef, setModalOpen, showModal } = useNavigation({
itemsArray: currentViewItemsArray,
showGrid: apps?.length > 0,
actionsLength: actionsArray?.length,
})

const { goToDefaultView } = useViewAndSectionHandler()

const handleVisibilityToggle = useCallback(
() => setModalOpen((open) => !open),
[setModalOpen]
)

useEffect(() => {
const activeItem = document.querySelector('.highlighted')
if (activeItem && typeof activeItem.scrollIntoView === 'function') {
activeItem?.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
}
})

useEffect(() => {
if (modalRef.current) {
modalRef.current?.focus()
}
})

const handleFocus = (event) => {
if (event.target === modalRef?.current) {
modalRef.current?.querySelector('input').focus()
}
}

useEffect(() => {
const handleKeyDown = (event) => {
if ((event.metaKey || event.ctrlKey) && event.key === '/') {
Expand Down Expand Up @@ -108,25 +83,9 @@ const CommandPalette = ({ apps, commands, shortcuts }) => {
<IconApps24 color={colors.white} />
</button>
{showModal ? (
<ModalContainer
ref={modalRef}
onFocus={handleFocus}
onKeyDown={handleKeyDown}
>
<ModalContainer ref={modalRef} onKeyDown={handleKeyDown}>
<div data-test="headerbar-menu" className="headerbar-menu">
<Search
value={filter}
onChange={handleFilterChange}
placeholder={
currentView === ALL_APPS_VIEW
? i18n.t('Search apps')
: currentView === ALL_COMMANDS_VIEW
? i18n.t('Search commands')
: currentView === ALL_SHORTCUTS_VIEW
? i18n.t('Search shortcuts')
: i18n.t('Search apps, shortcuts, commands')
}
/>
<SearchFilter />
<div className="headerbar-menu-content">
{currentView !== HOME_VIEW && !filter ? (
<BackButton onClickHandler={goToDefaultView} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import PropTypes from 'prop-types'
import React, { createContext, useContext, useState } from 'react'
import React, { createContext, useContext, useMemo, useState } from 'react'
import { HOME_VIEW } from '../utils/constants.js'

const commandPaletteContext = createContext()
Expand All @@ -10,20 +10,26 @@ export const CommandPaletteContextProvider = ({ children }) => {
const [currentView, setCurrentView] = useState(HOME_VIEW)
// home view sections
const [activeSection, setActiveSection] = useState(null)
const [showGrid, setShowGrid] = useState(null)

const contextValue = useMemo(
() => ({
filter,
setFilter,
highlightedIndex,
setHighlightedIndex,
currentView,
setCurrentView,
activeSection,
setActiveSection,
showGrid,
setShowGrid,
}),
[filter, highlightedIndex, currentView, activeSection, showGrid]
)

return (
<commandPaletteContext.Provider
value={{
filter,
setFilter,
highlightedIndex,
setHighlightedIndex,
currentView,
setCurrentView,
activeSection,
setActiveSection,
}}
>
<commandPaletteContext.Provider value={contextValue}>
{children}
</commandPaletteContext.Provider>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const useModal = (modalRef) => {
if (modalOpen) {
handleOpenModal()
} else {
handleCloseModal
handleCloseModal()
}
}, [modalOpen, handleCloseModal, handleOpenModal])

Expand Down
16 changes: 6 additions & 10 deletions components/header-bar/src/command-palette/hooks/use-navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import {
import useGrid from './use-grid.js'
import useListNavigation from './use-list-navigation.js'
import useModal from './use-modal.js'
import useViewHandler from './use-view-handler.js'
import useViewAndSectionHandler from './use-view-handler.js'

export const useNavigation = ({ itemsArray, showGrid, actionsLength }) => {
export const useNavigation = ({ itemsArray, actionsLength }) => {
const modalRef = useRef(null)

const {
Expand All @@ -22,13 +22,12 @@ export const useNavigation = ({ itemsArray, showGrid, actionsLength }) => {
highlightedIndex,
setHighlightedIndex,
setActiveSection,
showGrid,
} = useCommandPaletteContext()

const { modalOpen, setModalOpen } = useModal(modalRef)

const defaultSection = showGrid ? GRID_SECTION : ACTIONS_SECTION

const goToDefaultView = useViewHandler({ section: defaultSection })
const { goToDefaultSection, goToDefaultView } = useViewAndSectionHandler()

const { nextLeftIndex, nextRightIndex, lastRowFirstIndex } = useGrid({
columns: GRID_COLUMNS,
Expand Down Expand Up @@ -147,14 +146,12 @@ export const useNavigation = ({ itemsArray, showGrid, actionsLength }) => {
if (event.key === 'Escape') {
event.preventDefault()
setModalOpen(false)
setActiveSection(defaultSection)
setHighlightedIndex(0)
goToDefaultSection()
}
},
[
activeSection,
actionsLength,
defaultSection,
handleListViewNavigation,
highlightedIndex,
nextLeftIndex,
Expand All @@ -163,8 +160,8 @@ export const useNavigation = ({ itemsArray, showGrid, actionsLength }) => {
setModalOpen,
showGrid,
switchActiveSection,
setActiveSection,
setHighlightedIndex,
goToDefaultSection,
]
)

Expand Down Expand Up @@ -216,7 +213,6 @@ export const useNavigation = ({ itemsArray, showGrid, actionsLength }) => {

return {
handleKeyDown,
goToDefaultView,
modalRef,
setModalOpen,
showModal: modalOpen,
Expand Down
37 changes: 22 additions & 15 deletions components/header-bar/src/command-palette/hooks/use-view-handler.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
import { useCallback } from 'react'
import { useCommandPaletteContext } from '../context/command-palette-context.js'
import { HOME_VIEW } from '../utils/constants.js'
import { ACTIONS_SECTION, GRID_SECTION, HOME_VIEW } from '../utils/constants.js'

const useViewHandler = ({ section }) => {
const { setHighlightedIndex, setFilter, setCurrentView, setActiveSection } =
useCommandPaletteContext()
const useViewAndSectionHandler = () => {
const {
setHighlightedIndex,
setFilter,
setCurrentView,
setActiveSection,
showGrid,
} = useCommandPaletteContext()

const goToDefaultSection = useCallback(() => {
const defaultSection = showGrid ? GRID_SECTION : ACTIONS_SECTION
setActiveSection(defaultSection)
setHighlightedIndex(0)
}, [showGrid, setActiveSection, setHighlightedIndex])

const goToDefaultView = useCallback(() => {
setFilter('')
setCurrentView(HOME_VIEW)
setActiveSection(section)
setHighlightedIndex(0)
}, [
setActiveSection,
setCurrentView,
setFilter,
setHighlightedIndex,
section,
])
goToDefaultSection()
}, [setCurrentView, setFilter, goToDefaultSection])

return goToDefaultView
return {
goToDefaultView,
goToDefaultSection,
}
}

export default useViewHandler
export default useViewAndSectionHandler
40 changes: 0 additions & 40 deletions components/header-bar/src/command-palette/sections/container.js

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { colors, elevations } from '@dhis2/ui-constants'
import PropTypes from 'prop-types'
import React, { forwardRef, useEffect } from 'react'

const ModalContainer = forwardRef(function ModalContainer(
{ children, onKeyDown },
ref
) {
useEffect(() => {
const activeItem = ref?.current?.querySelector('.highlighted')
if (activeItem && typeof activeItem.scrollIntoView === 'function') {
activeItem?.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
}
}, [ref])

useEffect(() => {
if (!ref.current) {
return
}
const dialog = ref.current

const handleFocus = (event) => {
if (event.target === ref?.current) {
ref.current?.querySelector('input').focus()
}
}

dialog.addEventListener('focus', handleFocus)
dialog.addEventListener('keydown', onKeyDown)

return () => {
dialog.removeEventListener('focus', handleFocus)
dialog.removeEventListener('keydown', onKeyDown)
}
}, [onKeyDown, ref])

return (
<>
<dialog ref={ref}>{children}</dialog>
<style jsx>{`
dialog {
display: flex;
flex-direction: row;
border: none;
border-radius: 1px;
padding: 1px;
width: 572px;
max-height: 544px;
margin: 0 auto;
border-radius: 3px;
background: ${colors.white};
box-shadow: ${elevations.e100};
margin-top: 92px;
}
`}</style>
</>
)
})

ModalContainer.propTypes = {
children: PropTypes.node,
onKeyDown: PropTypes.func,
}

export default ModalContainer
Loading

0 comments on commit d3114bb

Please sign in to comment.