mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-30 04:35:42 +00:00 
			
		
		
		
	[UI] Search shortcut (#10181)
* Add search shortcut - Use '/' to open search drawer * Add user setting for spotlight * Hide spotlight if disabled * Updated playwright tests
This commit is contained in:
		| @@ -191,6 +191,12 @@ USER_SETTINGS: dict[str, InvenTreeSettingsKeyType] = { | ||||
|         'default': False, | ||||
|         'validator': bool, | ||||
|     }, | ||||
|     'SHOW_SPOTLIGHT': { | ||||
|         'name': _('Show Spotlight'), | ||||
|         'description': _('Enable spotlight navigation functionality'), | ||||
|         'default': True, | ||||
|         'validator': bool, | ||||
|     }, | ||||
|     'ICONS_IN_NAVBAR': { | ||||
|         'name': _('Navigation Icons'), | ||||
|         'description': _('Display icons in the navigation bar'), | ||||
|   | ||||
| @@ -8,7 +8,11 @@ import { | ||||
|   Tooltip, | ||||
|   UnstyledButton | ||||
| } from '@mantine/core'; | ||||
| import { useDisclosure, useDocumentVisibility } from '@mantine/hooks'; | ||||
| import { | ||||
|   useDisclosure, | ||||
|   useDocumentVisibility, | ||||
|   useHotkeys | ||||
| } from '@mantine/hooks'; | ||||
| import { IconBell, IconSearch } from '@tabler/icons-react'; | ||||
| import { useQuery } from '@tanstack/react-query'; | ||||
| import { type ReactNode, useEffect, useMemo, useState } from 'react'; | ||||
| @@ -49,11 +53,27 @@ export function Header() { | ||||
|   const [server] = useServerApiState(useShallow((state) => [state.server])); | ||||
|   const [navDrawerOpened, { open: openNavDrawer, close: closeNavDrawer }] = | ||||
|     useDisclosure(navigationOpen); | ||||
|  | ||||
|   const [ | ||||
|     searchDrawerOpened, | ||||
|     { open: openSearchDrawer, close: closeSearchDrawer } | ||||
|   ] = useDisclosure(false); | ||||
|  | ||||
|   useHotkeys([ | ||||
|     [ | ||||
|       '/', | ||||
|       () => { | ||||
|         openSearchDrawer(); | ||||
|       } | ||||
|     ], | ||||
|     [ | ||||
|       'mod+/', | ||||
|       () => { | ||||
|         openSearchDrawer(); | ||||
|       } | ||||
|     ] | ||||
|   ]); | ||||
|  | ||||
|   const [ | ||||
|     notificationDrawerOpened, | ||||
|     { open: openNotificationDrawer, close: closeNotificationDrawer } | ||||
| @@ -154,7 +174,7 @@ export function Header() { | ||||
|                 <IconSearch /> | ||||
|               </ActionIcon> | ||||
|             </Tooltip> | ||||
|             <SpotlightButton /> | ||||
|             {userSettings.isSet('SHOW_SPOTLIGHT') && <SpotlightButton />} | ||||
|             {globalSettings.isSet('BARCODE_ENABLE') && <ScanButton />} | ||||
|             <Indicator | ||||
|               radius='lg' | ||||
|   | ||||
| @@ -7,6 +7,7 @@ import { Navigate, Outlet, useLocation, useNavigate } from 'react-router-dom'; | ||||
|  | ||||
| import { getActions } from '../../defaults/actions'; | ||||
| import * as classes from '../../main.css'; | ||||
| import { useUserSettingsState } from '../../states/SettingsStates'; | ||||
| import { useUserState } from '../../states/UserState'; | ||||
| import { Boundary } from '../Boundary'; | ||||
| import { Footer } from './Footer'; | ||||
| @@ -37,6 +38,7 @@ export const [firstStore, firstSpotlight] = createSpotlight(); | ||||
| export default function LayoutComponent() { | ||||
|   const navigate = useNavigate(); | ||||
|   const location = useLocation(); | ||||
|   const userSettings = useUserSettingsState(); | ||||
|  | ||||
|   const defaultActions = getActions(navigate); | ||||
|   const [actions, setActions] = useState(defaultActions); | ||||
| @@ -68,17 +70,19 @@ export default function LayoutComponent() { | ||||
|         </Container> | ||||
|         <Space h='xl' /> | ||||
|         <Footer /> | ||||
|         <Spotlight | ||||
|           actions={actions} | ||||
|           store={firstStore} | ||||
|           highlightQuery | ||||
|           searchProps={{ | ||||
|             leftSection: <IconSearch size='1.2rem' />, | ||||
|             placeholder: t`Search...` | ||||
|           }} | ||||
|           shortcut={['mod + K', '/']} | ||||
|           nothingFound={t`Nothing found...`} | ||||
|         /> | ||||
|         {userSettings.isSet('SHOW_SPOTLIGHT') && ( | ||||
|           <Spotlight | ||||
|             actions={actions} | ||||
|             store={firstStore} | ||||
|             highlightQuery | ||||
|             searchProps={{ | ||||
|               leftSection: <IconSearch size='1.2rem' />, | ||||
|               placeholder: t`Search...` | ||||
|             }} | ||||
|             shortcut={['mod + K']} | ||||
|             nothingFound={t`Nothing found...`} | ||||
|           /> | ||||
|         )} | ||||
|       </Flex> | ||||
|     </ProtectedRoute> | ||||
|   ); | ||||
|   | ||||
| @@ -58,6 +58,7 @@ export default function UserSettings() { | ||||
|               'ICONS_IN_NAVBAR', | ||||
|               'STICKY_HEADER', | ||||
|               'STICKY_TABLE_HEADER', | ||||
|               'SHOW_SPOTLIGHT', | ||||
|               'DATE_DISPLAY_FORMAT', | ||||
|               'FORMS_CLOSE_USING_ESCAPE', | ||||
|               'DISPLAY_STOCKTAKE_TAB', | ||||
|   | ||||
| @@ -5,7 +5,7 @@ import { doCachedLogin } from './login.js'; | ||||
| /** | ||||
|  * Test for integration of django admin button | ||||
|  */ | ||||
| test('Admin Button', async ({ browser }) => { | ||||
| test('General - Admin Button', async ({ browser }) => { | ||||
|   const page = await doCachedLogin(browser, { | ||||
|     username: 'admin', | ||||
|     password: 'inventree', | ||||
| @@ -21,12 +21,17 @@ test('Admin Button', async ({ browser }) => { | ||||
| }); | ||||
|  | ||||
| // Tests for the global search functionality | ||||
| test('Search', async ({ browser }) => { | ||||
| test('General - Search', async ({ browser }) => { | ||||
|   const page = await doCachedLogin(browser, { | ||||
|     username: 'steven', | ||||
|     password: 'wizardstaff' | ||||
|   }); | ||||
|  | ||||
|   // Open the search drawer with a shortcut | ||||
|   await page.keyboard.press('Control+/'); | ||||
|   await page.getByRole('textbox', { name: 'global-search-input' }).waitFor(); | ||||
|   await page.keyboard.press('Escape'); | ||||
|  | ||||
|   await globalSearch(page, 'another customer'); | ||||
|  | ||||
|   // Check for expected results | ||||
|   | ||||
		Reference in New Issue
	
	Block a user