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