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

feat: add commands support and make logout searchable #1659

Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const CommandPalette = ({ apps, commands, shortcuts }) => {
setOpenModal,
itemsArray: currentViewItemsArray,
showGrid: apps?.length > 0,
actionsLength: actionsArray?.length,
actionsArray,
})

useEffect(() => {
Expand Down
9 changes: 9 additions & 0 deletions components/header-bar/src/command-palette/commands/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import useLogout from './useLogout.js'

const useCommands = () => {
const { logout } = useLogout()

return [logout]
}

export default useCommands
29 changes: 29 additions & 0 deletions components/header-bar/src/command-palette/commands/useLogout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { clearSensitiveCaches, useConfig } from '@dhis2/app-runtime'
import { colors } from '@dhis2/ui-constants'
import { IconLogOut16 } from '@dhis2/ui-icons'
import React from 'react'
import { joinPath } from '../../join-path.js'

const useLogout = () => {
const { baseUrl } = useConfig()

const href = joinPath(baseUrl, 'dhis-web-commons-security/logout.action')
const logoutAction = async () => {
await clearSensitiveCaches()
window.location.assign(href)
}

return {
logout: {
name: 'Logout',
icon: <IconLogOut16 color={colors.grey700} />,
description: 'Logout of the application',
action: logoutAction,
url: href,
},
logoutURL: href,
logoutAction,
}
}

export default useLogout
16 changes: 16 additions & 0 deletions components/header-bar/src/command-palette/hooks/use-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,20 @@
} from '@dhis2/ui-icons'
import React, { useMemo } from 'react'
import i18n from '../../locales/index.js'
import useLogout from '../commands/useLogout.js'
import { useCommandPaletteContext } from '../context/command-palette-context.js'
import { MIN_APPS_NUM } from './use-navigation.js'

export const useAvailableActions = ({ apps, shortcuts, commands }) => {
const { setCurrentView, setHighlightedIndex } = useCommandPaletteContext()

const switchViewAction = (type) => {
setCurrentView(type)
setHighlightedIndex(0)
}

const { logoutAction, logoutURL } = useLogout()

const actions = useMemo(() => {
const actionsArray = []
if (apps?.length > MIN_APPS_NUM) {
Expand All @@ -18,6 +29,7 @@
title: i18n.t('Browse apps'),
icon: <IconApps16 color={colors.grey700} />,
dataTest: 'headerbar-browse-apps',
action: () => switchViewAction('apps'),
})
}
if (commands?.length > 0) {
Expand All @@ -26,6 +38,7 @@
title: i18n.t('Browse commands'),
icon: <IconTerminalWindow16 color={colors.grey700} />,
dataTest: 'headerbar-browse-commands',
action: () => switchViewAction('commands'),
})
}
if (shortcuts?.length > 0) {
Expand All @@ -34,6 +47,7 @@
title: i18n.t('Browse shortcuts'),
icon: <IconRedo16 color={colors.grey700} />,
dataTest: 'headerbar-browse-shortcuts',
action: () => switchViewAction('shortcuts'),
})
}
// default logout action
Expand All @@ -42,8 +56,10 @@
title: i18n.t('Logout'),
icon: <IconLogOut16 color={colors.grey700} />,
dataTest: 'headerbar-logout',
action: logoutAction,
href: logoutURL,
})
return actionsArray
}, [apps, shortcuts, commands])

Check warning on line 63 in components/header-bar/src/command-palette/hooks/use-actions.js

View workflow job for this annotation

GitHub Actions / lint

React Hook useMemo has missing dependencies: 'logoutAction', 'logoutURL', and 'switchViewAction'. Either include them or remove the dependency array
return actions
}
35 changes: 20 additions & 15 deletions components/header-bar/src/command-palette/hooks/use-navigation.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useRef } from 'react'
import { useCallback, useEffect, useMemo, useRef } from 'react'
import { useCommandPaletteContext } from '../context/command-palette-context.js'

export const GRID_ITEMS_LENGTH = 8
Expand All @@ -8,7 +8,7 @@
setOpenModal,
itemsArray,
showGrid,
actionsLength,
actionsArray,
}) => {
const modalRef = useRef(null)

Expand All @@ -23,6 +23,15 @@
setActiveSection,
} = useCommandPaletteContext()

const actionsLength = actionsArray?.length || 0

const activeItems = useMemo(() => {
if (currentView === 'home' && activeSection === 'actions') {
return actionsArray
}
return itemsArray
}, [itemsArray, actionsArray, currentView, activeSection])

// highlight first item in filtered results
useEffect(() => {
setHighlightedIndex(0)
Expand Down Expand Up @@ -184,7 +193,7 @@
setHighlightedIndex(0)
}
},
[

Check warning on line 196 in components/header-bar/src/command-palette/hooks/use-navigation.js

View workflow job for this annotation

GitHub Actions / lint

React Hook useCallback has a missing dependency: 'showGrid'. Either include it or remove the dependency array
activeSection,
actionsLength,
defaultSection,
Expand All @@ -198,14 +207,12 @@

const handleKeyDown = useCallback(
(event) => {
const modal = modalRef.current

if (currentView === 'home') {
if (filter.length > 0) {
// search mode
handleListViewNavigation({
event,
listLength: itemsArray.length,
listLength: activeItems.length,
})
} else {
handleHomeViewNavigation(event)
Expand All @@ -214,23 +221,21 @@
setActiveSection(null)
handleListViewNavigation({
event,
listLength: itemsArray.length,
listLength: activeItems.length,
})
}

if (event.key === 'Enter') {
if (activeSection === 'actions') {
modal
?.querySelector('.actions-menu')
?.childNodes?.[highlightedIndex]?.click()
} else {
// open apps, shortcuts link
window.open(itemsArray[highlightedIndex]?.['defaultAction'])
// TODO: execute commands
const activeItem = activeItems[highlightedIndex]

if (activeItem?.['action']) {
activeItem?.['action']?.()
} else if (activeItem?.['defaultAction']) {
window.open(activeItem?.['defaultAction'])
}
}
},
[

Check warning on line 238 in components/header-bar/src/command-palette/hooks/use-navigation.js

View workflow job for this annotation

GitHub Actions / lint

React Hook useCallback has unnecessary dependencies: 'activeSection', 'goToDefaultView', 'setOpenModal', and 'showGrid'. Either exclude them or remove the dependency array
activeSection,
currentView,
filter.length,
Expand All @@ -238,7 +243,7 @@
handleHomeViewNavigation,
handleListViewNavigation,
highlightedIndex,
itemsArray,
activeItems,
setActiveSection,
setOpenModal,
showGrid,
Expand Down
38 changes: 25 additions & 13 deletions components/header-bar/src/command-palette/sections/list.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,32 @@ function List({ filteredItems, type }) {
<div data-test="headerbar-list">
{filteredItems.map(
(
{ displayName, name, defaultAction, icon, description },
{
displayName,
name,
defaultAction,
icon,
description,
url,
},
idx
) => (
<ListItem
type={type}
key={`app-${name}-${idx}`}
title={displayName || name}
path={defaultAction}
image={icon}
description={description}
highlighted={highlightedIndex === idx}
handleMouseEnter={() => setHighlightedIndex(idx)}
/>
)
) => {
const isImage = typeof icon === 'string'
const isIcon = React.isValidElement(icon)
return (
<ListItem
type={type}
key={`app-${name}-${idx}`}
title={displayName || name}
path={defaultAction || url}
image={isImage ? icon : undefined}
icon={isIcon ? icon : undefined}
description={description}
highlighted={highlightedIndex === idx}
handleMouseEnter={() => setHighlightedIndex(idx)}
/>
)
}
)}
</div>
)
Expand Down
39 changes: 6 additions & 33 deletions components/header-bar/src/command-palette/views/home-view.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { clearSensitiveCaches, useConfig } from '@dhis2/app-runtime'
import { spacers } from '@dhis2/ui-constants'
import PropTypes from 'prop-types'
import React from 'react'
import { joinPath } from '../../join-path.js'
import i18n from '../../locales/index.js'
import { useCommandPaletteContext } from '../context/command-palette-context.js'
import AppItem from '../sections/app-item.js'
Expand All @@ -11,10 +9,8 @@ import ListItem from '../sections/list-item.js'
import ListView from './list-view.js'

function HomeView({ apps, commands, shortcuts, actions }) {
const { baseUrl } = useConfig()
const {
filter,
setCurrentView,
highlightedIndex,
setHighlightedIndex,
activeSection,
Expand Down Expand Up @@ -79,41 +75,18 @@ function HomeView({ apps, commands, shortcuts, actions }) {
data-test="headerbar-actions-menu"
>
{actions.map(
({ dataTest, icon, title, type }, index) => {
const logoutActionHandler = async () => {
await clearSensitiveCaches()
window.location.assign(
joinPath(
baseUrl,
'dhis-web-commons-security/logout.action'
)
)
}

const viewActionHandler = () => {
setCurrentView(type)
setHighlightedIndex(0)
}

(
{ dataTest, icon, title, type, href, action },
index
) => {
return (
<ListItem
key={`action-${type}-${index}`}
title={title}
icon={icon}
dataTest={dataTest}
href={
type === 'logout'
? joinPath(
baseUrl,
'dhis-web-commons-security/logout.action'
)
: undefined
}
onClickHandler={
type === 'logout'
? logoutActionHandler
: viewActionHandler
}
href={href}
onClickHandler={action}
highlighted={
activeSection === 'actions' &&
highlightedIndex === index
Expand Down
3 changes: 2 additions & 1 deletion components/header-bar/src/header-bar.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { colors } from '@dhis2/ui-constants'
import PropTypes from 'prop-types'
import React, { useMemo } from 'react'
import CommandPalette from './command-palette/command-palette.js'
import useCommands from './command-palette/commands/index.js'
import { CommandPaletteContextProvider } from './command-palette/context/command-palette-context.js'
import { HeaderBarContextProvider } from './header-bar-context.js'
import { joinPath } from './join-path.js'
Expand Down Expand Up @@ -57,7 +58,7 @@ export const HeaderBar = ({
}, [data, baseUrl])

// fetch commands
const commands = []
const commands = useCommands()

// fetch shortcuts
const shortcuts = []
Expand Down
Loading