diff --git a/src/core/utils.js b/src/core/utils.js index d24ab6be..cf2cc843 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -623,13 +623,19 @@ export const parseSearch = () => { continue } i = params[i].split("=") - map[decodeURIComponent(i[0])] = decodeURIComponent(i[1]) + map[decodeURIComponent(i[0])] = (i[1] && decodeURIComponent(i[1])) || "" } } return map } +export const serializeSearch = (searchMap) => { + return Object.keys(searchMap).map(k => { + return encodeURIComponent(k) + "=" + encodeURIComponent(searchMap[k]) + }).join("&") +} + export const btoa = (str) => { let buffer diff --git a/src/plugins/topbar/topbar.jsx b/src/plugins/topbar/topbar.jsx index 9c18ad08..2977f55d 100644 --- a/src/plugins/topbar/topbar.jsx +++ b/src/plugins/topbar/topbar.jsx @@ -3,6 +3,7 @@ import PropTypes from "prop-types" //import "./topbar.less" import Logo from "./logo_small.png" +import {parseSearch, serializeSearch} from "../../core/utils" export default class Topbar extends React.Component { @@ -41,6 +42,12 @@ export default class Topbar extends React.Component { e.preventDefault() } + setSearch = (spec) => { + let search = parseSearch() + search["urls.primaryName"] = spec.name + window.location.search = serializeSearch(search) + } + setSelectedUrl = (selectedUrl) => { const configs = this.props.getConfigs() const urls = configs.urls || [] @@ -52,6 +59,7 @@ export default class Topbar extends React.Component { if(spec.url === selectedUrl) { this.setState({selectedIndex: i}) + this.setSearch(spec) } }) } diff --git a/test/core/utils.js b/test/core/utils.js index f0529f41..99ee60d4 100644 --- a/test/core/utils.js +++ b/test/core/utils.js @@ -3,6 +3,8 @@ import expect from "expect" import { fromJS, OrderedMap } from "immutable" import { mapToList, + parseSearch, + serializeSearch, validatePattern, validateMinLength, validateMaxLength, @@ -940,6 +942,48 @@ describe("utils", function() { }) }) + describe("parse and serialize search", function() { + afterEach(function() { + win.location.search = "" + }) + + describe("parsing", function() { + it("works with empty search", function() { + win.location.search = "" + expect(parseSearch()).toEqual({}) + }) + + it("works with only one key", function() { + win.location.search = "?foo" + expect(parseSearch()).toEqual({foo: ""}) + }) + + it("works with keys and values", function() { + win.location.search = "?foo=fooval&bar&baz=bazval" + expect(parseSearch()).toEqual({foo: "fooval", bar: "", baz: "bazval"}) + }) + + it("decode url encoded components", function() { + win.location.search = "?foo=foo%20bar" + expect(parseSearch()).toEqual({foo: "foo bar"}) + }) + }) + + describe("serializing", function() { + it("works with empty map", function() { + expect(serializeSearch({})).toEqual("") + }) + + it("works with multiple keys with and without values", function() { + expect(serializeSearch({foo: "", bar: "barval"})).toEqual("foo=&bar=barval") + }) + + it("encode url components", function() { + expect(serializeSearch({foo: "foo bar"})).toEqual("foo=foo%20bar") + }) + }) + }) + describe("sanitizeUrl", function() { it("should sanitize a `javascript:` url", function() { const res = sanitizeUrl("javascript:alert('bam!')")