Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFilipa Lacerda <filipa@gitlab.com>2018-05-03 20:33:33 +0300
committerFilipa Lacerda <filipa@gitlab.com>2018-05-03 20:33:33 +0300
commitbb3beb5e9e50204819e5358479849bc6c95e6bef (patch)
tree07ea055a813490c2b55650e93bbb630fc54e029b
parent48308728f202235fc55d8347ead1a1a428561205 (diff)
parentb91d400ee0c1fc10f20f9f1e909b01d0a4082ed4 (diff)
Merge branch 'ide-tree-changes-count' into 'master'
Added changes count to web IDE folder See merge request gitlab-org/gitlab-ce!18452
-rw-r--r--app/assets/javascripts/ide/components/changed_file_icon.vue10
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/list_item.vue2
-rw-r--r--app/assets/javascripts/ide/components/repo_file.vue89
-rw-r--r--app/assets/javascripts/ide/stores/getters.js17
-rw-r--r--app/assets/javascripts/ide/stores/utils.js10
-rw-r--r--app/assets/stylesheets/pages/repo.scss14
-rw-r--r--spec/javascripts/ide/components/repo_file_spec.js30
-rw-r--r--spec/javascripts/ide/stores/getters_spec.js63
8 files changed, 203 insertions, 32 deletions
diff --git a/app/assets/javascripts/ide/components/changed_file_icon.vue b/app/assets/javascripts/ide/components/changed_file_icon.vue
index fdbc14a4b8f..1cec84706fc 100644
--- a/app/assets/javascripts/ide/components/changed_file_icon.vue
+++ b/app/assets/javascripts/ide/components/changed_file_icon.vue
@@ -43,7 +43,7 @@ export default {
return `${this.changedIcon}-solid`;
},
changedIconClass() {
- return `multi-${this.changedIcon} prepend-left-5 pull-left`;
+ return `multi-${this.changedIcon} pull-left`;
},
tooltipTitle() {
if (!this.showTooltip) return undefined;
@@ -79,13 +79,7 @@ export default {
class="ide-file-changed-icon"
>
<icon
- v-if="file.staged && showStagedIcon"
- :name="stagedIcon"
- :size="12"
- :css-classes="changedIconClass"
- />
- <icon
- v-if="file.changed || file.tempFile || (file.staged && !showStagedIcon)"
+ v-if="file.changed || file.tempFile || file.staged"
:name="changedIcon"
:size="12"
:css-classes="changedIconClass"
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
index ad4713c40d5..872302840e2 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
@@ -36,7 +36,7 @@ export default {
return this.file.tempFile ? `file-addition${prefix}` : `file-modified${prefix}`;
},
iconClass() {
- return `multi-file-${this.file.tempFile ? 'additions' : 'modified'} append-right-8`;
+ return `multi-file-${this.file.tempFile ? 'addition' : 'modified'} append-right-8`;
},
},
methods: {
diff --git a/app/assets/javascripts/ide/components/repo_file.vue b/app/assets/javascripts/ide/components/repo_file.vue
index e86db2da4a6..89c5ce70dd3 100644
--- a/app/assets/javascripts/ide/components/repo_file.vue
+++ b/app/assets/javascripts/ide/components/repo_file.vue
@@ -1,22 +1,29 @@
<script>
-import { mapActions } from 'vuex';
-import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
-import fileIcon from '~/vue_shared/components/file_icon.vue';
+import { mapActions, mapGetters } from 'vuex';
+import { n__, __, sprintf } from '~/locale';
+import tooltip from '~/vue_shared/directives/tooltip';
+import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
+import Icon from '~/vue_shared/components/icon.vue';
+import FileIcon from '~/vue_shared/components/file_icon.vue';
import router from '../ide_router';
-import newDropdown from './new_dropdown/index.vue';
-import fileStatusIcon from './repo_file_status_icon.vue';
-import changedFileIcon from './changed_file_icon.vue';
-import mrFileIcon from './mr_file_icon.vue';
+import NewDropdown from './new_dropdown/index.vue';
+import FileStatusIcon from './repo_file_status_icon.vue';
+import ChangedFileIcon from './changed_file_icon.vue';
+import MrFileIcon from './mr_file_icon.vue';
export default {
name: 'RepoFile',
+ directives: {
+ tooltip,
+ },
components: {
- skeletonLoadingContainer,
- newDropdown,
- fileStatusIcon,
- fileIcon,
- changedFileIcon,
- mrFileIcon,
+ SkeletonLoadingContainer,
+ NewDropdown,
+ FileStatusIcon,
+ FileIcon,
+ ChangedFileIcon,
+ MrFileIcon,
+ Icon,
},
props: {
file: {
@@ -29,6 +36,34 @@ export default {
},
},
computed: {
+ ...mapGetters([
+ 'getChangesInFolder',
+ 'getUnstagedFilesCountForPath',
+ 'getStagedFilesCountForPath',
+ ]),
+ folderUnstagedCount() {
+ return this.getUnstagedFilesCountForPath(this.file.path);
+ },
+ folderStagedCount() {
+ return this.getStagedFilesCountForPath(this.file.path);
+ },
+ changesCount() {
+ return this.getChangesInFolder(this.file.path);
+ },
+ folderChangesTooltip() {
+ if (this.changesCount === 0) return undefined;
+
+ if (this.folderUnstagedCount > 0 && this.folderStagedCount === 0) {
+ return n__('%d unstaged change', '%d unstaged changes', this.folderUnstagedCount);
+ } else if (this.folderUnstagedCount === 0 && this.folderStagedCount > 0) {
+ return n__('%d staged change', '%d staged changes', this.folderStagedCount);
+ }
+
+ return sprintf(__('%{unstaged} unstaged and %{staged} staged changes'), {
+ unstaged: this.folderUnstagedCount,
+ staged: this.folderStagedCount,
+ });
+ },
isTree() {
return this.file.type === 'tree';
},
@@ -48,10 +83,19 @@ export default {
'is-open': this.file.opened,
};
},
+ showTreeChangesCount() {
+ return this.isTree && this.changesCount > 0 && !this.file.opened;
+ },
+ showChangedFileIcon() {
+ return this.file.changed || this.file.tempFile || this.file.staged;
+ },
},
updated() {
if (this.file.type === 'blob' && this.file.active) {
- this.$el.scrollIntoView();
+ this.$el.scrollIntoView({
+ behavior: 'smooth',
+ block: 'nearest',
+ });
}
},
methods: {
@@ -101,8 +145,23 @@ export default {
<mr-file-icon
v-if="file.mrChange"
/>
+ <span
+ v-if="showTreeChangesCount"
+ class="ide-tree-changes"
+ >
+ {{ changesCount }}
+ <icon
+ v-tooltip
+ :title="folderChangesTooltip"
+ data-container="body"
+ data-placement="right"
+ name="file-modified"
+ :size="12"
+ css-classes="prepend-left-5 multi-file-modified"
+ />
+ </span>
<changed-file-icon
- v-if="file.changed || file.tempFile || file.staged"
+ v-else-if="showChangedFileIcon"
:file="file"
:show-tooltip="true"
:show-staged-icon="true"
diff --git a/app/assets/javascripts/ide/stores/getters.js b/app/assets/javascripts/ide/stores/getters.js
index ec1ea155aee..a93d29fd865 100644
--- a/app/assets/javascripts/ide/stores/getters.js
+++ b/app/assets/javascripts/ide/stores/getters.js
@@ -1,4 +1,5 @@
import { __ } from '~/locale';
+import { getChangesCountForFiles, filePathMatches } from './utils';
export const activeFile = state => state.openFiles.find(file => file.active) || null;
@@ -55,7 +56,23 @@ export const allBlobs = state =>
}, [])
.sort((a, b) => b.lastOpenedAt - a.lastOpenedAt);
+export const getChangedFile = state => path => state.changedFiles.find(f => f.path === path);
export const getStagedFile = state => path => state.stagedFiles.find(f => f.path === path);
+export const getChangesInFolder = state => path => {
+ const changedFilesCount = state.changedFiles.filter(f => filePathMatches(f, path)).length;
+ const stagedFilesCount = state.stagedFiles.filter(
+ f => filePathMatches(f, path) && !getChangedFile(state)(f.path),
+ ).length;
+
+ return changedFilesCount + stagedFilesCount;
+};
+
+export const getUnstagedFilesCountForPath = state => path =>
+ getChangesCountForFiles(state.changedFiles, path);
+
+export const getStagedFilesCountForPath = state => path =>
+ getChangesCountForFiles(state.stagedFiles, path);
+
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js
index 59185f8f0ad..bc79ff4a542 100644
--- a/app/assets/javascripts/ide/stores/utils.js
+++ b/app/assets/javascripts/ide/stores/utils.js
@@ -33,7 +33,6 @@ export const dataStructure = () => ({
raw: '',
content: '',
parentTreeUrl: '',
- parentPath: '',
renderError: false,
base64: false,
editorRow: 1,
@@ -43,6 +42,7 @@ export const dataStructure = () => ({
viewMode: 'edit',
previewMode: null,
size: 0,
+ parentPath: null,
lastOpenedAt: 0,
});
@@ -83,7 +83,6 @@ export const decorateData = entity => {
opened,
active,
parentTreeUrl,
- parentPath,
changed,
renderError,
content,
@@ -91,6 +90,7 @@ export const decorateData = entity => {
previewMode,
file_lock,
html,
+ parentPath,
};
};
@@ -137,3 +137,9 @@ export const sortTree = sortedTree =>
}),
)
.sort(sortTreesByTypeAndName);
+
+export const filePathMatches = (f, path) =>
+ f.path.replace(new RegExp(`${f.name}$`), '').indexOf(`${path}/`) === 0;
+
+export const getChangesCountForFiles = (files, path) =>
+ files.filter(f => filePathMatches(f, path)).length;
diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss
index 2c0b20809ba..e74606e864f 100644
--- a/app/assets/stylesheets/pages/repo.scss
+++ b/app/assets/stylesheets/pages/repo.scss
@@ -603,14 +603,14 @@
}
}
-.multi-file-additions,
-.multi-file-additions-solid {
- fill: $green-500;
+.multi-file-addition,
+.multi-file-addition-solid {
+ color: $green-500;
}
.multi-file-modified,
.multi-file-modified-solid {
- fill: $orange-500;
+ color: $orange-500;
}
.multi-file-commit-list-collapsed {
@@ -991,6 +991,12 @@
resize: none;
}
+.ide-tree-changes {
+ display: flex;
+ align-items: center;
+ font-size: 12px;
+}
+
.ide-new-modal-label {
line-height: 34px;
}
diff --git a/spec/javascripts/ide/components/repo_file_spec.js b/spec/javascripts/ide/components/repo_file_spec.js
index ff391cb4351..28ff06e1f80 100644
--- a/spec/javascripts/ide/components/repo_file_spec.js
+++ b/spec/javascripts/ide/components/repo_file_spec.js
@@ -48,6 +48,33 @@ describe('RepoFile', () => {
});
});
+ describe('folder', () => {
+ it('renders changes count inside folder', () => {
+ const f = {
+ ...file('folder'),
+ path: 'testing',
+ type: 'tree',
+ branchId: 'master',
+ projectId: 'project',
+ };
+
+ store.state.changedFiles.push({
+ ...file('fileName'),
+ path: 'testing/fileName',
+ });
+
+ createComponent({
+ file: f,
+ level: 0,
+ });
+
+ const treeChangesEl = vm.$el.querySelector('.ide-tree-changes');
+
+ expect(treeChangesEl).not.toBeNull();
+ expect(treeChangesEl.textContent).toContain('1');
+ });
+ });
+
describe('locked file', () => {
let f;
@@ -72,8 +99,7 @@ describe('RepoFile', () => {
it('renders a tooltip', () => {
expect(
- vm.$el.querySelector('.ide-file-name span:nth-child(2)').dataset
- .originalTitle,
+ vm.$el.querySelector('.ide-file-name span:nth-child(2)').dataset.originalTitle,
).toContain('Locked by testuser');
});
});
diff --git a/spec/javascripts/ide/stores/getters_spec.js b/spec/javascripts/ide/stores/getters_spec.js
index b6b4dd28729..bd834443730 100644
--- a/spec/javascripts/ide/stores/getters_spec.js
+++ b/spec/javascripts/ide/stores/getters_spec.js
@@ -84,4 +84,67 @@ describe('IDE store getters', () => {
expect(getters.allBlobs(localState)[0].name).toBe('blob');
});
});
+
+ describe('getChangesInFolder', () => {
+ it('returns length of changed files for a path', () => {
+ localState.changedFiles.push(
+ {
+ path: 'test/index',
+ name: 'index',
+ },
+ {
+ path: 'app/123',
+ name: '123',
+ },
+ );
+
+ expect(getters.getChangesInFolder(localState)('test')).toBe(1);
+ });
+
+ it('returns length of changed & staged files for a path', () => {
+ localState.changedFiles.push(
+ {
+ path: 'test/index',
+ name: 'index',
+ },
+ {
+ path: 'testing/123',
+ name: '123',
+ },
+ );
+
+ localState.stagedFiles.push(
+ {
+ path: 'test/123',
+ name: '123',
+ },
+ {
+ path: 'test/index',
+ name: 'index',
+ },
+ {
+ path: 'testing/12345',
+ name: '12345',
+ },
+ );
+
+ expect(getters.getChangesInFolder(localState)('test')).toBe(2);
+ });
+
+ it('returns length of changed & tempFiles files for a path', () => {
+ localState.changedFiles.push(
+ {
+ path: 'test/index',
+ name: 'index',
+ },
+ {
+ path: 'test/newfile',
+ name: 'newfile',
+ tempFile: true,
+ },
+ );
+
+ expect(getters.getChangesInFolder(localState)('test')).toBe(2);
+ });
+ });
});