Fixed mutation for root
- fixed test spec - added warning only if file is deleted
This commit is contained in:
parent
c082ce08c8
commit
d9ba40aa04
9 changed files with 172 additions and 32 deletions
|
@ -1,7 +1,6 @@
|
|||
<script>
|
||||
import { mapActions } from 'vuex';
|
||||
import icon from '~/vue_shared/components/icon.vue';
|
||||
import newModal from './modal.vue';
|
||||
import upload from './upload.vue';
|
||||
import ItemButton from './button.vue';
|
||||
import { modalTypes } from '../../constants';
|
||||
|
@ -9,7 +8,6 @@ import { modalTypes } from '../../constants';
|
|||
export default {
|
||||
components: {
|
||||
icon,
|
||||
newModal,
|
||||
upload,
|
||||
ItemButton,
|
||||
},
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<script>
|
||||
import $ from 'jquery';
|
||||
import { __ } from '~/locale';
|
||||
import flash from '~/flash';
|
||||
import { __, sprintf, s__ } from '~/locale';
|
||||
import { mapActions, mapState, mapGetters } from 'vuex';
|
||||
import GlModal from '~/vue_shared/components/gl_modal.vue';
|
||||
import { modalTypes } from '../../constants';
|
||||
|
@ -15,15 +16,17 @@ export default {
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(['entryModal']),
|
||||
...mapState(['entries', 'entryModal']),
|
||||
...mapGetters('fileTemplates', ['templateTypes']),
|
||||
entryName: {
|
||||
get() {
|
||||
const entryPath = this.entryModal.entry.path;
|
||||
|
||||
if (this.entryModal.type === modalTypes.rename) {
|
||||
return this.name || this.entryModal.entry.name;
|
||||
return this.name || entryPath;
|
||||
}
|
||||
|
||||
return this.name || (this.entryModal.path !== '' ? `${this.entryModal.path}/` : '');
|
||||
return this.name || (entryPath ? `${entryPath}/` : '');
|
||||
},
|
||||
set(val) {
|
||||
this.name = val;
|
||||
|
@ -62,10 +65,40 @@ export default {
|
|||
...mapActions(['createTempEntry', 'renameEntry']),
|
||||
submitForm() {
|
||||
if (this.entryModal.type === modalTypes.rename) {
|
||||
this.renameEntry({
|
||||
path: this.entryModal.entry.path,
|
||||
name: this.entryName,
|
||||
});
|
||||
if (this.entries[this.entryName] && !this.entries[this.entryName].deleted) {
|
||||
flash(
|
||||
sprintf(s__('The name %{entryName} is already taken in this directory.'), {
|
||||
entryName: this.entryName,
|
||||
}),
|
||||
'alert',
|
||||
document,
|
||||
null,
|
||||
false,
|
||||
true,
|
||||
);
|
||||
} else {
|
||||
let parentPath = this.entryName.split('/');
|
||||
const entryName = parentPath.pop();
|
||||
parentPath = parentPath.join('/');
|
||||
|
||||
const createPromise =
|
||||
parentPath && !this.entries[parentPath]
|
||||
? this.createTempEntry({ name: parentPath, type: 'tree' })
|
||||
: Promise.resolve();
|
||||
|
||||
createPromise
|
||||
.then(() =>
|
||||
this.renameEntry({
|
||||
path: this.entryModal.entry.path,
|
||||
name: entryName,
|
||||
entryPath: null,
|
||||
parentPath,
|
||||
}),
|
||||
)
|
||||
.catch(() =>
|
||||
flash(__('Error creating a new path'), 'alert', document, null, false, true),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.createTempEntry({
|
||||
name: this.name,
|
||||
|
@ -82,7 +115,14 @@ export default {
|
|||
$('#ide-new-entry').modal('toggle');
|
||||
},
|
||||
focusInput() {
|
||||
const name = this.entries[this.entryName] ? this.entries[this.entryName].name : null;
|
||||
const inputValue = this.$refs.fieldName.value;
|
||||
|
||||
this.$refs.fieldName.focus();
|
||||
|
||||
if (name) {
|
||||
this.$refs.fieldName.setSelectionRange(inputValue.indexOf(name), inputValue.length);
|
||||
}
|
||||
},
|
||||
closedModal() {
|
||||
this.name = '';
|
||||
|
|
|
@ -215,15 +215,27 @@ export const deleteEntry = ({ commit, dispatch, state }, path) => {
|
|||
|
||||
export const resetOpenFiles = ({ commit }) => commit(types.RESET_OPEN_FILES);
|
||||
|
||||
export const renameEntry = ({ dispatch, commit, state }, { path, name, entryPath = null }) => {
|
||||
export const renameEntry = (
|
||||
{ dispatch, commit, state },
|
||||
{ path, name, entryPath = null, parentPath },
|
||||
) => {
|
||||
const entry = state.entries[entryPath || path];
|
||||
|
||||
commit(types.RENAME_ENTRY, { path, name, entryPath });
|
||||
commit(types.RENAME_ENTRY, { path, name, entryPath, parentPath });
|
||||
|
||||
if (entry.type === 'tree') {
|
||||
state.entries[entryPath || path].tree.forEach(f =>
|
||||
dispatch('renameEntry', { path, name, entryPath: f.path }),
|
||||
);
|
||||
const slashedParentPath = parentPath ? `${parentPath}/` : '';
|
||||
const targetEntry = entryPath ? entryPath.split('/').pop() : name;
|
||||
const newParentPath = `${slashedParentPath}${targetEntry}`;
|
||||
|
||||
state.entries[entryPath || path].tree.forEach(f => {
|
||||
dispatch('renameEntry', {
|
||||
path,
|
||||
name,
|
||||
entryPath: f.path,
|
||||
parentPath: newParentPath,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (!entryPath && !entry.tempFile) {
|
||||
|
|
|
@ -206,19 +206,17 @@ export default {
|
|||
}
|
||||
}
|
||||
},
|
||||
[types.RENAME_ENTRY](state, { path, name, entryPath = null }) {
|
||||
[types.RENAME_ENTRY](state, { path, name, entryPath = null, parentPath }) {
|
||||
const oldEntry = state.entries[entryPath || path];
|
||||
const nameRegex =
|
||||
!entryPath && oldEntry.type === 'blob'
|
||||
? new RegExp(`${oldEntry.name}$`)
|
||||
: new RegExp(`^${path}`);
|
||||
const newPath = oldEntry.path.replace(nameRegex, name);
|
||||
const parentPath = oldEntry.parentPath ? oldEntry.parentPath.replace(nameRegex, name) : '';
|
||||
const slashedParentPath = parentPath ? `${parentPath}/` : '';
|
||||
const newPath = entryPath
|
||||
? `${slashedParentPath}${oldEntry.name}`
|
||||
: `${slashedParentPath}${name}`;
|
||||
|
||||
state.entries[newPath] = {
|
||||
...oldEntry,
|
||||
id: newPath,
|
||||
key: `${name}-${oldEntry.type}-${oldEntry.id}`,
|
||||
key: `${newPath}-${oldEntry.type}-${oldEntry.id}`,
|
||||
path: newPath,
|
||||
name: entryPath ? oldEntry.name : name,
|
||||
tempFile: true,
|
||||
|
@ -228,6 +226,7 @@ export default {
|
|||
parentPath,
|
||||
raw: '',
|
||||
};
|
||||
|
||||
oldEntry.moved = true;
|
||||
oldEntry.movedPath = newPath;
|
||||
|
||||
|
@ -256,6 +255,7 @@ export default {
|
|||
Vue.delete(state.entries, oldEntry.path);
|
||||
}
|
||||
},
|
||||
|
||||
...projectMutations,
|
||||
...mergeRequestMutation,
|
||||
...fileMutations,
|
||||
|
|
5
changelogs/unreleased/49397-move-files-in-ide.yml
Normal file
5
changelogs/unreleased/49397-move-files-in-ide.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Resolve Move files in the Web IDE
|
||||
merge_request: 25431
|
||||
author:
|
||||
type: added
|
|
@ -3133,6 +3133,9 @@ msgstr ""
|
|||
msgid "Error Tracking"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error creating a new path"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error deleting %{issuableType}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -7384,6 +7387,9 @@ msgstr ""
|
|||
msgid "The maximum file size allowed is 200KB."
|
||||
msgstr ""
|
||||
|
||||
msgid "The name %{entryName} is already taken in this directory."
|
||||
msgstr ""
|
||||
|
||||
msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -18,6 +18,9 @@ describe('new file modal component', () => {
|
|||
store.state.entryModal = {
|
||||
type,
|
||||
path: '',
|
||||
entry: {
|
||||
path: '',
|
||||
},
|
||||
};
|
||||
|
||||
vm = createComponentWithStore(Component, store).$mount();
|
||||
|
@ -74,6 +77,7 @@ describe('new file modal component', () => {
|
|||
entry: {
|
||||
name: 'test',
|
||||
type: 'blob',
|
||||
path: 'test-path',
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -97,7 +101,7 @@ describe('new file modal component', () => {
|
|||
|
||||
describe('entryName', () => {
|
||||
it('returns entries name', () => {
|
||||
expect(vm.entryName).toBe('test');
|
||||
expect(vm.entryName).toBe('test-path');
|
||||
});
|
||||
|
||||
it('updated name', () => {
|
||||
|
@ -107,4 +111,53 @@ describe('new file modal component', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('submitForm', () => {
|
||||
it('throws an error when target entry exists', () => {
|
||||
const store = createStore();
|
||||
store.state.entryModal = {
|
||||
type: 'rename',
|
||||
path: 'test-path/test',
|
||||
entry: {
|
||||
name: 'test',
|
||||
type: 'blob',
|
||||
path: 'test-path/test',
|
||||
},
|
||||
};
|
||||
store.state.entries = {
|
||||
'test-path/test': {
|
||||
name: 'test',
|
||||
deleted: false,
|
||||
},
|
||||
};
|
||||
|
||||
vm = createComponentWithStore(Component, store).$mount();
|
||||
const flashSpy = spyOnDependency(modal, 'flash');
|
||||
vm.submitForm();
|
||||
|
||||
expect(flashSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('calls createTempEntry when target path does not exist', () => {
|
||||
const store = createStore();
|
||||
store.state.entryModal = {
|
||||
type: 'rename',
|
||||
path: 'test-path/test',
|
||||
entry: {
|
||||
name: 'test',
|
||||
type: 'blob',
|
||||
path: 'test-path1/test',
|
||||
},
|
||||
};
|
||||
|
||||
vm = createComponentWithStore(Component, store).$mount();
|
||||
spyOn(vm, 'createTempEntry').and.callFake(() => Promise.resolve());
|
||||
vm.submitForm();
|
||||
|
||||
expect(vm.createTempEntry).toHaveBeenCalledWith({
|
||||
name: 'test-path1',
|
||||
type: 'tree',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -499,12 +499,12 @@ describe('Multi-file store actions', () => {
|
|||
|
||||
testAction(
|
||||
renameEntry,
|
||||
{ path: 'test', name: 'new-name' },
|
||||
{ path: 'test', name: 'new-name', entryPath: null, parentPath: 'parent-path' },
|
||||
store.state,
|
||||
[
|
||||
{
|
||||
type: types.RENAME_ENTRY,
|
||||
payload: { path: 'test', name: 'new-name', entryPath: null },
|
||||
payload: { path: 'test', name: 'new-name', entryPath: null, parentPath: 'parent-path' },
|
||||
},
|
||||
],
|
||||
[{ type: 'deleteEntry', payload: 'test' }],
|
||||
|
@ -527,17 +527,33 @@ describe('Multi-file store actions', () => {
|
|||
|
||||
testAction(
|
||||
renameEntry,
|
||||
{ path: 'test', name: 'new-name' },
|
||||
{ path: 'test', name: 'new-name', parentPath: 'parent-path' },
|
||||
store.state,
|
||||
[
|
||||
{
|
||||
type: types.RENAME_ENTRY,
|
||||
payload: { path: 'test', name: 'new-name', entryPath: null },
|
||||
payload: { path: 'test', name: 'new-name', entryPath: null, parentPath: 'parent-path' },
|
||||
},
|
||||
],
|
||||
[
|
||||
{ type: 'renameEntry', payload: { path: 'test', name: 'new-name', entryPath: 'tree-1' } },
|
||||
{ type: 'renameEntry', payload: { path: 'test', name: 'new-name', entryPath: 'tree-2' } },
|
||||
{
|
||||
type: 'renameEntry',
|
||||
payload: {
|
||||
path: 'test',
|
||||
name: 'new-name',
|
||||
entryPath: 'tree-1',
|
||||
parentPath: 'parent-path/new-name',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'renameEntry',
|
||||
payload: {
|
||||
path: 'test',
|
||||
name: 'new-name',
|
||||
entryPath: 'tree-2',
|
||||
parentPath: 'parent-path/new-name',
|
||||
},
|
||||
},
|
||||
{ type: 'deleteEntry', payload: 'test' },
|
||||
],
|
||||
done,
|
||||
|
|
|
@ -298,7 +298,12 @@ describe('Multi-file store mutations', () => {
|
|||
});
|
||||
|
||||
it('creates new renamed entry', () => {
|
||||
mutations.RENAME_ENTRY(localState, { path: 'oldPath', name: 'newPath' });
|
||||
mutations.RENAME_ENTRY(localState, {
|
||||
path: 'oldPath',
|
||||
name: 'newPath',
|
||||
entryPath: null,
|
||||
parentPath: '',
|
||||
});
|
||||
|
||||
expect(localState.entries.newPath).toEqual({
|
||||
...localState.entries.oldPath,
|
||||
|
@ -335,7 +340,12 @@ describe('Multi-file store mutations', () => {
|
|||
...file(),
|
||||
};
|
||||
|
||||
mutations.RENAME_ENTRY(localState, { path: 'oldPath', name: 'newPath' });
|
||||
mutations.RENAME_ENTRY(localState, {
|
||||
path: 'oldPath',
|
||||
name: 'newPath',
|
||||
entryPath: null,
|
||||
parentPath: 'parentPath',
|
||||
});
|
||||
|
||||
expect(localState.entries.parentPath.tree.length).toBe(1);
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue