mirror of https://github.com/grafana/grafana
Navigation: Proof-of-concept for pinning navbar items (#44775)
parent
7c826cb43f
commit
b6682cdcb9
@ -1,15 +1,32 @@ |
|||||||
import { createSlice } from '@reduxjs/toolkit'; |
import { createSlice, PayloadAction } from '@reduxjs/toolkit'; |
||||||
import { NavModelItem } from '@grafana/data'; |
import { NavModelItem } from '@grafana/data'; |
||||||
import config from 'app/core/config'; |
import config from 'app/core/config'; |
||||||
|
|
||||||
export const initialState: NavModelItem[] = config.bootData.navTree; |
const defaultPins = ['home', 'dashboards', 'explore', 'alerting'].join(','); |
||||||
|
const storedPins = (window.localStorage.getItem('pinnedNavItems') ?? defaultPins).split(','); |
||||||
|
|
||||||
|
export const initialState: NavModelItem[] = (config.bootData.navTree as NavModelItem[]).map((n: NavModelItem) => ({ |
||||||
|
...n, |
||||||
|
hideFromNavbar: n.id === undefined || !storedPins.includes(n.id), |
||||||
|
})); |
||||||
|
|
||||||
const navTreeSlice = createSlice({ |
const navTreeSlice = createSlice({ |
||||||
name: 'navBarTree', |
name: 'navBarTree', |
||||||
initialState, |
initialState, |
||||||
reducers: {}, |
reducers: { |
||||||
|
togglePin: (state, action: PayloadAction<{ id: string }>) => { |
||||||
|
const navItemIndex = state.findIndex((navItem) => navItem.id === action.payload.id); |
||||||
|
state[navItemIndex].hideFromNavbar = !state[navItemIndex].hideFromNavbar; |
||||||
|
window.localStorage.setItem( |
||||||
|
'pinnedNavItems', |
||||||
|
state |
||||||
|
.filter((n) => !n.hideFromNavbar) |
||||||
|
.map((n) => n.id) |
||||||
|
.join(',') |
||||||
|
); |
||||||
|
}, |
||||||
|
}, |
||||||
}); |
}); |
||||||
|
|
||||||
export const {} = navTreeSlice.actions; |
export const { togglePin } = navTreeSlice.actions; |
||||||
|
|
||||||
export const navTreeReducer = navTreeSlice.reducer; |
export const navTreeReducer = navTreeSlice.reducer; |
||||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,35 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import { render as rtlRender } from '@testing-library/react'; |
||||||
|
import { AnyAction, configureStore } from '@reduxjs/toolkit'; |
||||||
|
import { ThunkMiddlewareFor } from '@reduxjs/toolkit/dist/getDefaultMiddleware'; |
||||||
|
import { Provider } from 'react-redux'; |
||||||
|
import { createRootReducer } from 'app/core/reducers/root'; |
||||||
|
import { StoreState } from 'app/types'; |
||||||
|
import { mockNavModel } from './mocks/navModel'; |
||||||
|
|
||||||
|
function render( |
||||||
|
ui: React.ReactElement, |
||||||
|
{ |
||||||
|
preloadedState = { navIndex: mockNavModel }, |
||||||
|
store = configureStore< |
||||||
|
StoreState, |
||||||
|
AnyAction, |
||||||
|
ReadonlyArray<ThunkMiddlewareFor<StoreState, { thunk: true; serializableCheck: false; immutableCheck: false }>> |
||||||
|
>({ |
||||||
|
reducer: createRootReducer(), |
||||||
|
preloadedState, |
||||||
|
middleware: (getDefaultMiddleware) => |
||||||
|
getDefaultMiddleware({ thunk: true, serializableCheck: false, immutableCheck: false }), |
||||||
|
}), |
||||||
|
...renderOptions |
||||||
|
}: { preloadedState?: Partial<StoreState>; store?: ReturnType<typeof configureStore> } = {} |
||||||
|
) { |
||||||
|
function Wrapper({ children }: { children: React.ReactNode }) { |
||||||
|
return <Provider store={store}>{children}</Provider>; |
||||||
|
} |
||||||
|
|
||||||
|
return rtlRender(ui, { wrapper: Wrapper, ...renderOptions }); |
||||||
|
} |
||||||
|
|
||||||
|
export * from '@testing-library/react'; |
||||||
|
export { render }; |
Loading…
Reference in new issue