Commit 5ec9adb7 by David Kaltschmidt

Explore: compact state URLs

- allow positional state array in URL
- key-based parsing as fallback
- fix issue where split state was kept in URL after closing split
parent f6d33256
......@@ -36,14 +36,40 @@ describe('state functions', () => {
range: DEFAULT_RANGE,
});
});
it('returns a valid Explore state from URL parameter', () => {
const paramValue =
'%7B"datasource":"Local","queries":%5B%7B"query":"metric"%7D%5D,"range":%7B"from":"now-1h","to":"now"%7D%7D';
expect(parseUrlState(paramValue)).toMatchObject({
datasource: 'Local',
queries: [{ query: 'metric' }],
range: {
from: 'now-1h',
to: 'now',
},
});
});
it('returns a valid Explore state from a compact URL parameter', () => {
const paramValue = '%5B"now-1h","now","Local","metric"%5D';
expect(parseUrlState(paramValue)).toMatchObject({
datasource: 'Local',
queries: [{ query: 'metric' }],
range: {
from: 'now-1h',
to: 'now',
},
});
});
});
describe('serializeStateToUrlParam', () => {
it('returns url parameter value for a state object', () => {
const state = {
...DEFAULT_EXPLORE_STATE,
datasourceName: 'foo',
range: {
from: 'now - 5h',
from: 'now-5h',
to: 'now',
},
queries: [
......@@ -57,10 +83,33 @@ describe('state functions', () => {
};
expect(serializeStateToUrlParam(state)).toBe(
'{"datasource":"foo","queries":[{"query":"metric{test=\\"a/b\\"}"},' +
'{"query":"super{foo=\\"x/z\\"}"}],"range":{"from":"now - 5h","to":"now"}}'
'{"query":"super{foo=\\"x/z\\"}"}],"range":{"from":"now-5h","to":"now"}}'
);
});
it('returns url parameter value for a state object', () => {
const state = {
...DEFAULT_EXPLORE_STATE,
datasourceName: 'foo',
range: {
from: 'now-5h',
to: 'now',
},
queries: [
{
query: 'metric{test="a/b"}',
},
{
query: 'super{foo="x/z"}',
},
],
};
expect(serializeStateToUrlParam(state, true)).toBe(
'["now-5h","now","foo","metric{test=\\"a/b\\"}","super{foo=\\"x/z\\"}"]'
);
});
});
describe('interplay', () => {
it('can parse the serialized state into the original state', () => {
const state = {
......
......@@ -60,7 +60,20 @@ export async function getExploreUrl(
export function parseUrlState(initial: string | undefined): ExploreUrlState {
if (initial) {
try {
return JSON.parse(decodeURI(initial));
const parsed = JSON.parse(decodeURI(initial));
if (Array.isArray(parsed)) {
if (parsed.length <= 3) {
throw new Error('Error parsing compact URL state for Explore.');
}
const range = {
from: parsed[0],
to: parsed[1],
};
const datasource = parsed[2];
const queries = parsed.slice(3).map(query => ({ query }));
return { datasource, queries, range };
}
return parsed;
} catch (e) {
console.error(e);
}
......@@ -68,11 +81,19 @@ export function parseUrlState(initial: string | undefined): ExploreUrlState {
return { datasource: null, queries: [], range: DEFAULT_RANGE };
}
export function serializeStateToUrlParam(state: ExploreState): string {
export function serializeStateToUrlParam(state: ExploreState, compact?: boolean): string {
const urlState: ExploreUrlState = {
datasource: state.datasourceName,
queries: state.queries.map(q => ({ query: q.query })),
range: state.range,
};
if (compact) {
return JSON.stringify([
urlState.range.from,
urlState.range.to,
urlState.datasource,
...urlState.queries.map(q => q.query),
]);
}
return JSON.stringify(urlState);
}
......@@ -275,7 +275,6 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
const { onChangeSplit } = this.props;
if (onChangeSplit) {
onChangeSplit(false);
this.saveState();
}
};
......@@ -292,7 +291,6 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
if (onChangeSplit) {
const state = this.cloneState();
onChangeSplit(true, state);
this.saveState();
}
};
......
......@@ -38,10 +38,17 @@ export class Wrapper extends Component<WrapperProps, WrapperState> {
onChangeSplit = (split: boolean, splitState: ExploreState) => {
this.setState({ split, splitState });
// When closing split, remove URL state for split part
if (!split) {
delete this.urlStates[STATE_KEY_RIGHT];
this.props.updateLocation({
query: this.urlStates,
});
}
};
onSaveState = (key: string, state: ExploreState) => {
const urlState = serializeStateToUrlParam(state);
const urlState = serializeStateToUrlParam(state, true);
this.urlStates[key] = urlState;
this.props.updateLocation({
query: this.urlStates,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment