vmui: add option to customize url params for individual pages (#2582)

This commit is contained in:
Yury Molodov 2022-05-17 14:13:15 +03:00 committed by Aliaksandr Valialkin
parent 87e4e76537
commit 8739fb8a91
No known key found for this signature in database
GPG key ID: A72BEC6CD3D0DED1
3 changed files with 54 additions and 24 deletions

View file

@ -19,29 +19,29 @@ import DashboardsLayout from "./components/PredefinedPanels/DashboardsLayout";
const App: FC = () => { const App: FC = () => {
return <> return <>
<CssBaseline /> {/* CSS Baseline: kind of normalize.css made by materialUI team - can be scoped */} <HashRouter>
<LocalizationProvider dateAdapter={DayjsUtils}> {/* Allows datepicker to work with DayJS */} <CssBaseline /> {/* CSS Baseline: kind of normalize.css made by materialUI team - can be scoped */}
<StyledEngineProvider injectFirst> <LocalizationProvider dateAdapter={DayjsUtils}> {/* Allows datepicker to work with DayJS */}
<ThemeProvider theme={THEME}> {/* Material UI theme customization */} <StyledEngineProvider injectFirst>
<StateProvider> {/* Serialized into query string, common app settings */} <ThemeProvider theme={THEME}> {/* Material UI theme customization */}
<AuthStateProvider> {/* Auth related info - optionally persisted to Local Storage */} <StateProvider> {/* Serialized into query string, common app settings */}
<GraphStateProvider> {/* Graph settings */} <AuthStateProvider> {/* Auth related info - optionally persisted to Local Storage */}
<SnackbarProvider> {/* Display various snackbars */} <GraphStateProvider> {/* Graph settings */}
<HashRouter> <SnackbarProvider> {/* Display various snackbars */}
<Routes> <Routes>
<Route path={"/"} element={<HomeLayout/>}> <Route path={"/"} element={<HomeLayout/>}>
<Route path={router.home} element={<CustomPanel/>}/> <Route path={router.home} element={<CustomPanel/>}/>
<Route path={router.dashboards} element={<DashboardsLayout/>}/> <Route path={router.dashboards} element={<DashboardsLayout/>}/>
</Route> </Route>
</Routes> </Routes>
</HashRouter> </SnackbarProvider>
</SnackbarProvider> </GraphStateProvider>
</GraphStateProvider> </AuthStateProvider>
</AuthStateProvider> </StateProvider>
</StateProvider> </ThemeProvider>
</ThemeProvider> </StyledEngineProvider>
</StyledEngineProvider> </LocalizationProvider>
</LocalizationProvider> </HashRouter>
</>; </>;
}; };

View file

@ -2,6 +2,7 @@ import React, {createContext, FC, useContext, useEffect, useMemo, useReducer} fr
import {Action, AppState, initialState, reducer} from "./reducer"; import {Action, AppState, initialState, reducer} from "./reducer";
import {getQueryStringValue, setQueryStringValue} from "../../utils/query-string"; import {getQueryStringValue, setQueryStringValue} from "../../utils/query-string";
import {Dispatch} from "react"; import {Dispatch} from "react";
import {useLocation} from "react-router-dom";
type StateContextType = { state: AppState, dispatch: Dispatch<Action> }; type StateContextType = { state: AppState, dispatch: Dispatch<Action> };
@ -17,12 +18,13 @@ export const initialPrepopulatedState = Object.entries(initialState)
}), {}) as AppState; }), {}) as AppState;
export const StateProvider: FC = ({children}) => { export const StateProvider: FC = ({children}) => {
const location = useLocation();
const [state, dispatch] = useReducer(reducer, initialPrepopulatedState); const [state, dispatch] = useReducer(reducer, initialPrepopulatedState);
useEffect(() => { useEffect(() => {
setQueryStringValue(state as unknown as Record<string, unknown>); setQueryStringValue(state as unknown as Record<string, unknown>);
}, [state]); }, [state, location]);
const contextValue = useMemo(() => { const contextValue = useMemo(() => {
return { state, dispatch }; return { state, dispatch };

View file

@ -1,12 +1,18 @@
import qs from "qs"; import qs from "qs";
import get from "lodash.get"; import get from "lodash.get";
import router from "../router";
const stateToUrlParams = { const graphStateToUrlParams = {
"time.duration": "range_input", "time.duration": "range_input",
"time.period.date": "end_input", "time.period.date": "end_input",
"time.period.step": "step_input", "time.period.step": "step_input",
"time.relativeTime": "relative_time", "time.relativeTime": "relative_time",
"displayType": "tab" "displayType": "tab",
};
const stateToUrlParams = {
[router.home]: graphStateToUrlParams,
[router.dashboards]: graphStateToUrlParams,
}; };
// TODO need function for detect types. // TODO need function for detect types.
@ -32,14 +38,23 @@ const stateToUrlParams = {
export const setQueryStringWithoutPageReload = (qsValue: string): void => { export const setQueryStringWithoutPageReload = (qsValue: string): void => {
const w = window; const w = window;
if (w) { if (w) {
const newurl = `${w.location.protocol}//${w.location.host}${w.location.pathname}?${qsValue}${w.location.hash}`; const qs = qsValue ? `?${qsValue}` : "";
const newurl = `${w.location.protocol}//${w.location.host}${w.location.pathname}${qs}${w.location.hash}`;
w.history.pushState({ path: newurl }, "", newurl); w.history.pushState({ path: newurl }, "", newurl);
} }
}; };
export const setQueryStringValue = (newValue: Record<string, unknown>): void => { export const setQueryStringValue = (newValue: Record<string, unknown>): void => {
const queryMap = new Map(Object.entries(stateToUrlParams)); const route = window.location.hash.replace("#", "");
const query = get(newValue, "query", "") as string[]; const params = stateToUrlParams[route] || {};
const queryMap = new Map(Object.entries(params));
const isGraphRoute = route === router.home || route === router.dashboards;
const newQsValue = isGraphRoute ? getGraphQsValue(newValue, queryMap) : getQsValue(newValue, queryMap);
setQueryStringWithoutPageReload(newQsValue.join("&"));
};
const getGraphQsValue = (newValue: Record<string, unknown>, queryMap: Map<string, string>): string[] => {
const query = get(newValue, "query", []) as string[];
const newQsValue: string[] = []; const newQsValue: string[] = [];
query.forEach((q, i) => { query.forEach((q, i) => {
queryMap.forEach((queryKey, stateKey) => { queryMap.forEach((queryKey, stateKey) => {
@ -52,7 +67,20 @@ export const setQueryStringValue = (newValue: Record<string, unknown>): void =>
newQsValue.push(`g${i}.expr=${encodeURIComponent(q)}`); newQsValue.push(`g${i}.expr=${encodeURIComponent(q)}`);
}); });
setQueryStringWithoutPageReload(newQsValue.join("&")); return newQsValue;
};
const getQsValue = (newValue: Record<string, unknown>, queryMap: Map<string, string>): string[] => {
const newQsValue: string[] = [];
queryMap.forEach((queryKey, stateKey) => {
const value = get(newValue, stateKey, "") as string;
if (value) {
const valueEncoded = encodeURIComponent(value);
newQsValue.push(`${queryKey}=${valueEncoded}`);
}
});
return newQsValue;
}; };
export const getQueryStringValue = ( export const getQueryStringValue = (