Page: Add inline rename functionality (#68828)

* initial attempt at inline rename

* handle version correctly

* refactor

* minor tweaks

* add unit tests

* prettier...

* add to other tabs, remove settings tab when feature toggle is enabled

* fix truncation

* allow title to span full width of page

* fix h1 styling when no renderTitle/onEditTitle is present

* better layout

* use input from grafana/ui, fix imports

* fix unit test

* better error handling

* don't use autosavefield

* undo changes to AutoSaveField

* remove timeout

* remove maxWidth now we're not using AutoSaveField

* rename isEditInProgress to isLoading

* sync localValue with value

* better responsive css
This commit is contained in:
Ashley Harrison
2023-05-31 18:03:54 +02:00
committed by GitHub
parent f29b058927
commit 10adebd7b3
11 changed files with 364 additions and 24 deletions

View File

@@ -12,7 +12,7 @@ import { buildNavModel, getDashboardsTabID } from '../folders/state/navModel';
import { useSearchStateManager } from '../search/state/SearchStateManager';
import { getSearchPlaceholder } from '../search/tempI18nPhrases';
import { skipToken, useGetFolderQuery } from './api/browseDashboardsAPI';
import { skipToken, useGetFolderQuery, useSaveFolderMutation } from './api/browseDashboardsAPI';
import { BrowseActions } from './components/BrowseActions/BrowseActions';
import { BrowseFilters } from './components/BrowseFilters';
import { BrowseView } from './components/BrowseView';
@@ -59,6 +59,7 @@ const BrowseDashboardsPage = memo(({ match }: Props) => {
}, [isSearching, searchState.result, stateManager]);
const { data: folderDTO } = useGetFolderQuery(folderUID ?? skipToken);
const [saveFolder] = useSaveFolderMutation();
const navModel = useMemo(() => {
if (!folderDTO) {
return undefined;
@@ -78,10 +79,25 @@ const BrowseDashboardsPage = memo(({ match }: Props) => {
const { canEditInFolder, canCreateDashboards, canCreateFolder } = getFolderPermissions(folderDTO);
const onEditTitle = folderUID
? async (newValue: string) => {
if (folderDTO) {
const result = await saveFolder({
...folderDTO,
title: newValue,
});
if ('error' in result) {
throw result.error;
}
}
}
: undefined;
return (
<Page
navId="dashboards/browse"
pageNav={navModel}
onEditTitle={onEditTitle}
actions={
<>
{folderDTO && <FolderActionsButton folder={folderDTO} />}

View File

@@ -7,15 +7,16 @@ import { useSelector } from 'app/types';
import { AlertsFolderView } from '../alerting/unified/AlertsFolderView';
import { useGetFolderQuery } from './api/browseDashboardsAPI';
import { useGetFolderQuery, useSaveFolderMutation } from './api/browseDashboardsAPI';
import { FolderActionsButton } from './components/FolderActionsButton';
export interface OwnProps extends GrafanaRouteComponentProps<{ uid: string }> {}
export function BrowseFolderAlertingPage({ match }: OwnProps) {
const { uid: folderUID } = match.params;
const { data: folderDTO, isLoading } = useGetFolderQuery(folderUID);
const { data: folderDTO } = useGetFolderQuery(folderUID);
const folder = useSelector((state) => state.folder);
const [saveFolder] = useSaveFolderMutation();
const navModel = useMemo(() => {
if (!folderDTO) {
@@ -32,13 +33,28 @@ export function BrowseFolderAlertingPage({ match }: OwnProps) {
return model;
}, [folderDTO]);
const onEditTitle = folderUID
? async (newValue: string) => {
if (folderDTO) {
const result = await saveFolder({
...folderDTO,
title: newValue,
});
if ('error' in result) {
throw result.error;
}
}
}
: undefined;
return (
<Page
navId="dashboards/browse"
pageNav={navModel}
onEditTitle={onEditTitle}
actions={<>{folderDTO && <FolderActionsButton folder={folderDTO} />}</>}
>
<Page.Contents isLoading={isLoading}>
<Page.Contents>
<AlertsFolderView folder={folder} />
</Page.Contents>
</Page>

View File

@@ -9,14 +9,15 @@ import { LibraryPanelsSearch } from '../library-panels/components/LibraryPanelsS
import { OpenLibraryPanelModal } from '../library-panels/components/OpenLibraryPanelModal/OpenLibraryPanelModal';
import { LibraryElementDTO } from '../library-panels/types';
import { useGetFolderQuery } from './api/browseDashboardsAPI';
import { useGetFolderQuery, useSaveFolderMutation } from './api/browseDashboardsAPI';
export interface OwnProps extends GrafanaRouteComponentProps<{ uid: string }> {}
export function BrowseFolderLibraryPanelsPage({ match }: OwnProps) {
const { uid: folderUID } = match.params;
const { data: folderDTO, isLoading } = useGetFolderQuery(folderUID);
const { data: folderDTO } = useGetFolderQuery(folderUID);
const [selected, setSelected] = useState<LibraryElementDTO | undefined>(undefined);
const [saveFolder] = useSaveFolderMutation();
const navModel = useMemo(() => {
if (!folderDTO) {
@@ -33,13 +34,28 @@ export function BrowseFolderLibraryPanelsPage({ match }: OwnProps) {
return model;
}, [folderDTO]);
const onEditTitle = folderUID
? async (newValue: string) => {
if (folderDTO) {
const result = await saveFolder({
...folderDTO,
title: newValue,
});
if ('error' in result) {
throw result.error;
}
}
}
: undefined;
return (
<Page
navId="dashboards/browse"
pageNav={navModel}
onEditTitle={onEditTitle}
actions={<>{folderDTO && <FolderActionsButton folder={folderDTO} />}</>}
>
<Page.Contents isLoading={isLoading}>
<Page.Contents>
<LibraryPanelsSearch
onClick={setSelected}
currentFolderUID={folderUID}

View File

@@ -40,6 +40,18 @@ export const browseDashboardsAPI = createApi({
query: (folderUID) => ({ url: `/folders/${folderUID}`, params: { accesscontrol: true } }),
providesTags: (_result, _error, arg) => [{ type: 'getFolder', id: arg }],
}),
saveFolder: builder.mutation<FolderDTO, FolderDTO>({
invalidatesTags: (_result, _error, args) => [{ type: 'getFolder', id: args.uid }],
query: (folder) => ({
method: 'PUT',
showErrorAlert: false,
url: `/folders/${folder.uid}`,
data: {
title: folder.title,
version: folder.version,
},
}),
}),
getAffectedItems: builder.query<DescendantCount, DashboardTreeSelection>({
queryFn: async (selectedItems) => {
const folderUIDs = Object.keys(selectedItems.folder).filter((uid) => selectedItems.folder[uid]);
@@ -80,5 +92,6 @@ export const browseDashboardsAPI = createApi({
}),
});
export const { endpoints, useGetAffectedItemsQuery, useGetFolderQuery, useMoveFolderMutation } = browseDashboardsAPI;
export const { endpoints, useGetAffectedItemsQuery, useGetFolderQuery, useMoveFolderMutation, useSaveFolderMutation } =
browseDashboardsAPI;
export { skipToken } from '@reduxjs/toolkit/query/react';

View File

@@ -63,16 +63,16 @@ export function buildNavModel(folder: FolderDTO, parents = folder.parents): NavM
url: `${folder.url}/permissions`,
});
}
}
if (folder.canSave) {
model.children!.push({
active: false,
icon: 'cog',
id: getSettingsTabID(folder.uid),
text: 'Settings',
url: `${folder.url}/settings`,
});
if (folder.canSave) {
model.children!.push({
active: false,
icon: 'cog',
id: getSettingsTabID(folder.uid),
text: 'Settings',
url: `${folder.url}/settings`,
});
}
}
return model;