Commit 7100c7a2 authored by Samantha Ming's avatar Samantha Ming Committed by Natalia Tepluhina

Keep details in MR when changing target branch

- Add defaultKey to Autosave
- Pass defaultKey in issuable form
parent 1284ea5a
/* eslint-disable no-param-reassign, no-void, consistent-return */
/* eslint-disable no-param-reassign, consistent-return */
import AccessorUtilities from './lib/utils/accessor';
export default class Autosave {
constructor(field, key) {
constructor(field, key, fallbackKey) {
this.field = field;
this.isLocalStorageAvailable = AccessorUtilities.isLocalStorageAccessSafe();
......@@ -11,6 +11,7 @@ export default class Autosave {
key = key.join('/');
}
this.key = `autosave/${key}`;
this.fallbackKey = fallbackKey;
this.field.data('autosave', this);
this.restore();
this.field.on('input', () => this.save());
......@@ -21,9 +22,12 @@ export default class Autosave {
if (!this.field.length) return;
const text = window.localStorage.getItem(this.key);
const fallbackText = window.localStorage.getItem(this.fallbackKey);
if ((text != null ? text.length : void 0) > 0) {
if (text) {
this.field.val(text);
} else if (fallbackText) {
this.field.val(fallbackText);
}
this.field.trigger('input');
......@@ -41,7 +45,10 @@ export default class Autosave {
const text = this.field.val();
if (this.isLocalStorageAvailable && (text != null ? text.length : void 0) > 0) {
if (this.isLocalStorageAvailable && text) {
if (this.fallbackKey) {
window.localStorage.setItem(this.fallbackKey, text);
}
return window.localStorage.setItem(this.key, text);
}
......@@ -51,6 +58,7 @@ export default class Autosave {
reset() {
if (!this.isLocalStorageAvailable) return;
window.localStorage.removeItem(this.fallbackKey);
return window.localStorage.removeItem(this.key);
}
......
......@@ -6,6 +6,36 @@ import UsersSelect from './users_select';
import ZenMode from './zen_mode';
import AutoWidthDropdownSelect from './issuable/auto_width_dropdown_select';
import { parsePikadayDate, pikadayToString } from './lib/utils/datetime_utility';
import { queryToObject, objectToQuery } from './lib/utils/url_utility';
function organizeQuery(obj, isFallbackKey = false) {
const sourceBranch = 'merge_request[source_branch]';
const targetBranch = 'merge_request[target_branch]';
if (isFallbackKey) {
return {
[sourceBranch]: obj[sourceBranch],
};
}
return {
[sourceBranch]: obj[sourceBranch],
[targetBranch]: obj[targetBranch],
};
}
function format(searchTerm, isFallbackKey = false) {
const queryObject = queryToObject(searchTerm);
const organizeQueryObject = organizeQuery(queryObject, isFallbackKey);
const formattedQuery = objectToQuery(organizeQueryObject);
return formattedQuery;
}
function getFallbackKey() {
const searchTerm = format(document.location.search, true);
return ['autosave', document.location.pathname, searchTerm].join('/');
}
export default class IssuableForm {
constructor(form) {
......@@ -57,16 +87,20 @@ export default class IssuableForm {
}
initAutosave() {
this.autosave = new Autosave(this.titleField, [
document.location.pathname,
document.location.search,
'title',
]);
return new Autosave(this.descriptionField, [
document.location.pathname,
document.location.search,
'description',
]);
const searchTerm = format(document.location.search);
const fallbackKey = getFallbackKey();
this.autosave = new Autosave(
this.titleField,
[document.location.pathname, searchTerm, 'title'],
`${fallbackKey}=title`,
);
return new Autosave(
this.descriptionField,
[document.location.pathname, searchTerm, 'description'],
`${fallbackKey}=description`,
);
}
handleSubmit() {
......
......@@ -181,4 +181,36 @@ export function getWebSocketUrl(path) {
return `${getWebSocketProtocol()}//${joinPaths(window.location.host, path)}`;
}
/**
* Convert search query into an object
*
* @param {String} query from "document.location.search"
* @returns {Object}
*
* ex: "?one=1&two=2" into {one: 1, two: 2}
*/
export function queryToObject(query) {
const removeQuestionMarkFromQuery = String(query).startsWith('?') ? query.slice(1) : query;
return removeQuestionMarkFromQuery.split('&').reduce((accumulator, curr) => {
const p = curr.split('=');
accumulator[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
return accumulator;
}, {});
}
/**
* Convert search query object back into a search query
*
* @param {Object} obj that needs to be converted
* @returns {String}
*
* ex: {one: 1, two: 2} into "one=1&two=2"
*
*/
export function objectToQuery(obj) {
return Object.keys(obj)
.map(k => `${encodeURIComponent(k)}=${encodeURIComponent(obj[k])}`)
.join('&');
}
export { joinPaths };
---
title: Keep details in MR when changing target branch
merge_request: 19138
author:
type: changed
......@@ -9,6 +9,7 @@ describe('Autosave', () => {
let autosave;
const field = $('<textarea></textarea>');
const key = 'key';
const fallbackKey = 'fallbackKey';
describe('class constructor', () => {
beforeEach(() => {
......@@ -22,6 +23,13 @@ describe('Autosave', () => {
expect(AccessorUtilities.isLocalStorageAccessSafe).toHaveBeenCalled();
expect(autosave.isLocalStorageAvailable).toBe(true);
});
it('should set .isLocalStorageAvailable if fallbackKey is passed', () => {
autosave = new Autosave(field, key, fallbackKey);
expect(AccessorUtilities.isLocalStorageAccessSafe).toHaveBeenCalled();
expect(autosave.isLocalStorageAvailable).toBe(true);
});
});
describe('restore', () => {
......@@ -151,4 +159,33 @@ describe('Autosave', () => {
});
});
});
describe('restore with fallbackKey', () => {
beforeEach(() => {
autosave = {
field,
key,
fallbackKey,
};
autosave.isLocalStorageAvailable = true;
});
it('should call .getItem', () => {
Autosave.prototype.restore.call(autosave);
expect(window.localStorage.getItem).toHaveBeenCalledWith(fallbackKey);
});
it('should call .setItem for key and fallbackKey', () => {
Autosave.prototype.save.call(autosave);
expect(window.localStorage.setItem).toHaveBeenCalledTimes(2);
});
it('should call .removeItem for key and fallbackKey', () => {
Autosave.prototype.reset.call(autosave);
expect(window.localStorage.removeItem).toHaveBeenCalledTimes(2);
});
});
});
......@@ -282,4 +282,20 @@ describe('URL utility', () => {
expect(urlUtils.getWebSocketUrl(path)).toEqual('ws://example.com/lorem/ipsum?a=bc');
});
});
describe('queryToObject', () => {
it('converts search query into an object', () => {
const searchQuery = '?one=1&two=2';
expect(urlUtils.queryToObject(searchQuery)).toEqual({ one: '1', two: '2' });
});
});
describe('objectToQuery', () => {
it('converts search query object back into a search query', () => {
const searchQueryObject = { one: '1', two: '2' };
expect(urlUtils.objectToQuery(searchQueryObject)).toEqual('one=1&two=2');
});
});
});
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