diff --git a/packages/url/CHANGELOG.md b/packages/url/CHANGELOG.md
index e0f2f033c4af94..0b1dfb761ff72b 100644
--- a/packages/url/CHANGELOG.md
+++ b/packages/url/CHANGELOG.md
@@ -1,3 +1,11 @@
+## 2.2.0 (Unreleased)
+
+### Features
+
+- Added `getQueryArg`.
+- Added `hasQueryArg`.
+- Added `removeQueryArgs`.
+
## 2.1.0 (2018-10-16)
### Features
diff --git a/packages/url/README.md b/packages/url/README.md
index 6c156612327551..26f54fbaff57f7 100644
--- a/packages/url/README.md
+++ b/packages/url/README.md
@@ -25,6 +25,15 @@ const newURL = addQueryArgs( 'https://google.com', { q: 'test' } ); // https://
// Prepends 'http://' to URLs that are probably mean to have them
const actualURL = prependHTTP( 'wordpress.org' ); // http://wordpress.org
+
+// Gets a single query arg from the given URL.
+const foo = getQueryArg( 'https://wordpress.org?foo=bar&bar=baz', 'foo' ); // bar
+
+// Checks whether a URL contains a given query arg.
+const hasBar = hasQueryArg( 'https://wordpress.org?foo=bar&bar=baz', 'bar' ); // true
+
+// Removes one or more query args from the given URL.
+const newUrl = removeQueryArgs( 'https://wordpress.org?foo=bar&bar=baz&baz=foobar', 'foo', 'bar' ); // https://wordpress.org?baz=foobar
```
diff --git a/packages/url/src/index.js b/packages/url/src/index.js
index 4538ee2040bbc6..98cd123cfc666d 100644
--- a/packages/url/src/index.js
+++ b/packages/url/src/index.js
@@ -21,10 +21,10 @@ export function isURL( url ) {
/**
* Appends arguments to the query string of the url
*
- * @param {string} url URL
- * @param {Object} args Query Args
+ * @param {string} url URL
+ * @param {Object} args Query Args
*
- * @return {string} Updated URL
+ * @return {string} Updated URL
*/
export function addQueryArgs( url, args ) {
const queryStringIndex = url.indexOf( '?' );
@@ -34,6 +34,51 @@ export function addQueryArgs( url, args ) {
return baseUrl + '?' + stringify( { ...query, ...args } );
}
+/**
+ * Returns a single query argument of the url
+ *
+ * @param {string} url URL
+ * @param {string} arg Query arg name
+ *
+ * @return {Array|string} Query arg value.
+ */
+export function getQueryArg( url, arg ) {
+ const queryStringIndex = url.indexOf( '?' );
+ const query = queryStringIndex !== -1 ? parse( url.substr( queryStringIndex + 1 ) ) : {};
+
+ return query[ arg ];
+}
+
+/**
+ * Determines whether the URL contains a given query arg.
+ *
+ * @param {string} url URL
+ * @param {string} arg Query arg name
+ *
+ * @return {boolean} Whether or not the URL contains the query aeg.
+ */
+export function hasQueryArg( url, arg ) {
+ return getQueryArg( url, arg ) !== undefined;
+}
+
+/**
+ * Removes arguments from the query string of the url
+ *
+ * @param {string} url URL
+ * @param {...string} args Query Args
+ *
+ * @return {string} Updated URL
+ */
+export function removeQueryArgs( url, ...args ) {
+ const queryStringIndex = url.indexOf( '?' );
+ const query = queryStringIndex !== -1 ? parse( url.substr( queryStringIndex + 1 ) ) : {};
+ const baseUrl = queryStringIndex !== -1 ? url.substr( 0, queryStringIndex ) : url;
+
+ args.forEach( ( arg ) => delete query[ arg ] );
+
+ return baseUrl + '?' + stringify( query );
+}
+
/**
* Prepends "http://" to a url, if it looks like something that is meant to be a TLD.
*
diff --git a/packages/url/src/test/index.test.js b/packages/url/src/test/index.test.js
index 7815a1cae68ed6..46548528f3bc10 100644
--- a/packages/url/src/test/index.test.js
+++ b/packages/url/src/test/index.test.js
@@ -9,6 +9,9 @@ import { every } from 'lodash';
import {
isURL,
addQueryArgs,
+ getQueryArg,
+ hasQueryArg,
+ removeQueryArgs,
prependHTTP,
safeDecodeURI,
} from '../';
@@ -69,6 +72,66 @@ describe( 'addQueryArgs', () => {
} );
} );
+describe( 'getQueryArg', () => {
+ it( 'should get the value of an existing query arg', () => {
+ const url = 'https://andalouses.example/beach?foo=bar&bar=baz';
+
+ expect( getQueryArg( url, 'foo' ) ).toBe( 'bar' );
+ } );
+
+ it( 'should not return a value of an unknown query arg', () => {
+ const url = 'https://andalouses.example/beach?foo=bar&bar=baz';
+
+ expect( getQueryArg( url, 'baz' ) ).toBeUndefined();
+ } );
+
+ it( 'should get the value of an arry query arg', () => {
+ const url = 'https://andalouses.example/beach?foo[]=bar&foo[]=baz';
+
+ expect( getQueryArg( url, 'foo' ) ).toEqual( [ 'bar', 'baz' ] );
+ } );
+} );
+
+describe( 'hasQueryArg', () => {
+ it( 'should return true for an existing query arg', () => {
+ const url = 'https://andalouses.example/beach?foo=bar&bar=baz';
+
+ expect( hasQueryArg( url, 'foo' ) ).toBeTruthy();
+ } );
+
+ it( 'should return false for an unknown query arg', () => {
+ const url = 'https://andalouses.example/beach?foo=bar&bar=baz';
+
+ expect( hasQueryArg( url, 'baz' ) ).toBeFalsy();
+ } );
+
+ it( 'should return true for an arry query arg', () => {
+ const url = 'https://andalouses.example/beach?foo[]=bar&foo[]=baz';
+
+ expect( hasQueryArg( url, 'foo' ) ).toBeTruthy();
+ } );
+} );
+
+describe( 'removeQueryArgs', () => {
+ it( 'should not change URL not containing query args', () => {
+ const url = 'https://andalouses.example/beach?foo=bar&bar=baz';
+
+ expect( removeQueryArgs( url, 'baz', 'test' ) ).toEqual( url );
+ } );
+
+ it( 'should remove existing query args', () => {
+ const url = 'https://andalouses.example/beach?foo=bar&baz=foo&bar=baz';
+
+ expect( removeQueryArgs( url, 'foo', 'bar' ) ).toEqual( 'https://andalouses.example/beach?baz=foo' );
+ } );
+
+ it( 'should remove array query arg', () => {
+ const url = 'https://andalouses.example/beach?foo[]=bar&foo[]=baz&bar=foobar';
+
+ expect( removeQueryArgs( url, 'foo' ) ).toEqual( 'https://andalouses.example/beach?bar=foobar' );
+ } );
+} );
+
describe( 'prependHTTP', () => {
it( 'should prepend http to a domain', () => {
const url = 'wordpress.org';