summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFilipa Lacerda <filipa@gitlab.com>2017-03-28 11:56:24 +0100
committerFilipa Lacerda <filipa@gitlab.com>2017-03-28 14:11:04 +0100
commitb64555d36a0b931dc087ddb48674c0fe36581e8c (patch)
treebd1fbd68b975728be0d086958ad955f80486f9f0
parentadec9194ef9b825a3a79dc262975987012639f23 (diff)
downloadgitlab-ce-b64555d36a0b931dc087ddb48674c0fe36581e8c.tar.gz
Adds show all button
-rw-r--r--app/assets/javascripts/environments/components/environment.js14
-rw-r--r--app/assets/javascripts/environments/components/environment_item.js13
-rw-r--r--app/assets/javascripts/environments/components/environments_table.js43
-rw-r--r--app/assets/javascripts/environments/services/environments_service.js14
-rw-r--r--app/assets/javascripts/environments/stores/environments_store.js46
-rw-r--r--app/assets/stylesheets/pages/environments.scss8
-rw-r--r--changelogs/unreleased/28732-expandable-folders.yml4
-rw-r--r--spec/javascripts/environments/environment_spec.js99
-rw-r--r--spec/javascripts/environments/environments_store_spec.js111
-rw-r--r--spec/javascripts/environments/mock_data.js16
10 files changed, 287 insertions, 81 deletions
diff --git a/app/assets/javascripts/environments/components/environment.js b/app/assets/javascripts/environments/components/environment.js
index 57599876f5a..9760b30f30c 100644
--- a/app/assets/javascripts/environments/components/environment.js
+++ b/app/assets/javascripts/environments/components/environment.js
@@ -78,11 +78,11 @@ export default Vue.component('environment-component', {
},
methods: {
- toggleFolder(folder) {
+ toggleFolder(folder, folderUrl) {
this.store.toggleFolder(folder);
if (!folder.isOpen) {
- this.fetchChildEnvironments(folder);
+ this.fetchChildEnvironments(folder, folderUrl);
}
},
@@ -125,14 +125,13 @@ export default Vue.component('environment-component', {
});
},
- fetchChildEnvironments(folder) {
+ fetchChildEnvironments(folder, folderUrl) {
this.isLoadingFolderContent = true;
- this.service.getFolderContent(folder.folderName)
+ this.service.getFolderContent(folderUrl)
.then(resp => resp.json())
.then((response) => {
- console.log(response);
- this.store.folderContent(folder, response.environments);
+ this.store.setfolderContent(folder, response.environments);
})
.then(() => {
this.isLoadingFolderContent = false;
@@ -204,7 +203,8 @@ export default Vue.component('environment-component', {
:environments="state.environments"
:can-create-deployment="canCreateDeploymentParsed"
:can-read-environment="canReadEnvironmentParsed"
- :service="service"/>
+ :service="service"
+ :is-loading-folder-content="isLoadingFolderContent" />
</div>
<table-pagination v-if="state.paginationInformation && state.paginationInformation.totalPages > 1"
diff --git a/app/assets/javascripts/environments/components/environment_item.js b/app/assets/javascripts/environments/components/environment_item.js
index 8cfaeb69233..3fe1042557e 100644
--- a/app/assets/javascripts/environments/components/environment_item.js
+++ b/app/assets/javascripts/environments/components/environment_item.js
@@ -392,6 +392,15 @@ export default {
return '';
},
+
+ /**
+ * Constructs folder URL based on the current location and the folder id.
+ *
+ * @return {String}
+ */
+ folderUrl() {
+ return `${window.location.pathname}/folders/${this.model.folderName}`;
+ },
},
/**
@@ -411,12 +420,12 @@ export default {
methods: {
onClickFolder() {
- eventHub.$emit('toggleFolder', this.model);
+ eventHub.$emit('toggleFolder', this.model, this.folderUrl);
},
},
template: `
- <tr>
+ <tr :class="{ 'child-row': model.isChildren }">
<td>
<a v-if="!model.isFolder"
class="environment-name"
diff --git a/app/assets/javascripts/environments/components/environments_table.js b/app/assets/javascripts/environments/components/environments_table.js
index d59cb6500a2..5a1f28d0bdf 100644
--- a/app/assets/javascripts/environments/components/environments_table.js
+++ b/app/assets/javascripts/environments/components/environments_table.js
@@ -31,6 +31,18 @@ export default {
type: Object,
required: true,
},
+
+ isLoadingFolderContent: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+
+ methods: {
+ folderUrl(model) {
+ return `${window.location.pathname}/folders/${model.folderName}`;
+ },
},
template: `
@@ -54,13 +66,30 @@ export default {
:can-read-environment="canReadEnvironment"
:service="service"></tr>
- <tr v-if="model.isFolder && model.isOpen && model.children && model.children.length > 0"
- is="environment-item"
- v-for="children in model.children"
- :model="children"
- :can-create-deployment="canCreateDeploymentParsed"
- :can-read-environment="canReadEnvironmentParsed"
- :service="service"></tr>
+ <template v-if="model.isFolder && model.isOpen && model.children && model.children.length > 0">
+ <tr v-if="isLoadingFolderContent">
+ <td colspan="6" class="text-center">
+ <i class="fa fa-spin fa-spinner" aria-hidden="true"/>
+ </td>
+ </tr>
+
+ <tr v-if="!isLoadingFolderContent"
+ is="environment-item"
+ v-for="children in model.children"
+ :model="children"
+ :can-create-deployment="canCreateDeployment"
+ :can-read-environment="canReadEnvironment"
+ :service="service"></tr>
+
+ <tr v-if="!isLoadingFolderContent">
+ <td colspan="6" class="text-center">
+ <a :href="folderUrl(model)" class="btn btn-default">
+ Show all
+ </a>
+ </td>
+ </tr>
+ </template>
+
</template>
</tbody>
</table>
diff --git a/app/assets/javascripts/environments/services/environments_service.js b/app/assets/javascripts/environments/services/environments_service.js
index d5729c9d1c5..4917df64a56 100644
--- a/app/assets/javascripts/environments/services/environments_service.js
+++ b/app/assets/javascripts/environments/services/environments_service.js
@@ -6,14 +6,7 @@ Vue.use(VueResource);
export default class EnvironmentsService {
constructor(endpoint) {
- /**
- * FIX ME: This should be sent by backend.
- */
- const customActions = {
- folderContent: { method: 'GET', url: `${window.location.pathname}/folders{/name}?perPage=2` },
- };
-
- this.environments = Vue.resource(endpoint, {}, customActions);
+ this.environments = Vue.resource(endpoint);
}
get(scope, page) {
@@ -24,8 +17,7 @@ export default class EnvironmentsService {
return Vue.http.post(endpoint, {}, { emulateJSON: true });
}
- getFolderContent(folderName) {
- debugger
- return this.environments.folderContent({ name: folderName });
+ getFolderContent(folderUrl) {
+ return Vue.http.get(`${folderUrl}.json?per_page=3`);
}
}
diff --git a/app/assets/javascripts/environments/stores/environments_store.js b/app/assets/javascripts/environments/stores/environments_store.js
index a1c0988f1f5..158e7922e3c 100644
--- a/app/assets/javascripts/environments/stores/environments_store.js
+++ b/app/assets/javascripts/environments/stores/environments_store.js
@@ -92,23 +92,53 @@ export default class EnvironmentsStore {
}
/**
- * Toggles folder open property given the given folder.
+ * Toggles folder open property for the given folder.
*
- * @param {String} envType
+ * @param {Object} folder
* @return {Array}
*/
toggleFolder(folder) {
return this.updateFolder(folder, 'isOpen', !folder.isOpen);
}
- folderContent(folder, environments) {
- debugger;
- return this.updateFolder(folder, 'children', environments);
+ /**
+ * Updates the folder with the received environments.
+ *
+ *
+ * @param {Object} folder Folder to update
+ * @param {Array} environments Received environments
+ * @return {Object}
+ */
+ setfolderContent(folder, environments) {
+ const updatedEnvironments = environments.map((env) => {
+ let updated = env;
+
+ if (env.latest) {
+ updated = Object.assign({}, env, env.latest);
+ delete updated.latest;
+ } else {
+ updated = env;
+ }
+
+ updated.isChildren = true;
+
+ return updated;
+ });
+
+ return this.updateFolder(folder, 'children', updatedEnvironments);
}
+ /**
+ * Given a folder a prop and a new value updates the correct folder.
+ *
+ * @param {Object} folder
+ * @param {String} prop
+ * @param {String|Boolean|Object|Array} newValue
+ * @return {Array}
+ */
updateFolder(folder, prop, newValue) {
const environments = this.state.environments;
- debugger;
+
const updatedEnvironments = environments.map((env) => {
const updateEnv = Object.assign({}, env);
if (env.isFolder && env.id === folder.id) {
@@ -117,10 +147,10 @@ export default class EnvironmentsStore {
return updateEnv;
});
- debugger;
- console.log(updatedEnvironments);
this.state.environments = updatedEnvironments;
+
+ return updatedEnvironments;
}
}
diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss
index 25be7f408d0..b2ec8f38e6a 100644
--- a/app/assets/stylesheets/pages/environments.scss
+++ b/app/assets/stylesheets/pages/environments.scss
@@ -54,6 +54,14 @@
}
}
+ .child-row td:first-child a {
+ margin-left: 17px;
+ }
+
+ .environments-show-all {
+ text-align: center;
+ }
+
.btn-group {
> a {
diff --git a/changelogs/unreleased/28732-expandable-folders.yml b/changelogs/unreleased/28732-expandable-folders.yml
new file mode 100644
index 00000000000..9ae30ba6253
--- /dev/null
+++ b/changelogs/unreleased/28732-expandable-folders.yml
@@ -0,0 +1,4 @@
+---
+title: Add back expandable folder behavior
+merge_request:
+author:
diff --git a/spec/javascripts/environments/environment_spec.js b/spec/javascripts/environments/environment_spec.js
index 9601575577e..37e01ef7516 100644
--- a/spec/javascripts/environments/environment_spec.js
+++ b/spec/javascripts/environments/environment_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import '~/flash';
import EnvironmentsComponent from '~/environments/components/environment';
-import { environment } from './mock_data';
+import { environment, folder } from './mock_data';
describe('Environment', () => {
preloadFixtures('static/environments/environments.html.raw');
@@ -175,4 +175,101 @@ describe('Environment', () => {
}, 0);
});
});
+
+ describe('expandable folders', () => {
+ const environmentsResponseInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify({
+ environments: [folder],
+ stopped_count: 0,
+ available_count: 1,
+ }), {
+ status: 200,
+ headers: {
+ 'X-nExt-pAge': '2',
+ 'x-page': '1',
+ 'X-Per-Page': '1',
+ 'X-Prev-Page': '',
+ 'X-TOTAL': '37',
+ 'X-Total-Pages': '2',
+ },
+ }));
+ };
+
+ beforeEach(() => {
+ Vue.http.interceptors.push(environmentsResponseInterceptor);
+ component = new EnvironmentsComponent({
+ el: document.querySelector('#environments-list-view'),
+ });
+ });
+
+ afterEach(() => {
+ Vue.http.interceptors = _.without(
+ Vue.http.interceptors, environmentsResponseInterceptor,
+ );
+ });
+
+ it('should open a closed folder', (done) => {
+ setTimeout(() => {
+ component.$el.querySelector('.folder-name').click();
+
+ Vue.nextTick(() => {
+ expect(
+ component.$el.querySelector('.folder-icon i.fa-caret-right').getAttribute('style'),
+ ).toContain('display: none');
+ expect(
+ component.$el.querySelector('.folder-icon i.fa-caret-down').getAttribute('style'),
+ ).not.toContain('display: none');
+ done();
+ });
+ });
+ });
+
+ it('should close an opened folder', (done) => {
+ setTimeout(() => {
+ // open folder
+ component.$el.querySelector('.folder-name').click();
+
+ Vue.nextTick(() => {
+ // close folder
+ component.$el.querySelector('.folder-name').click();
+
+ Vue.nextTick(() => {
+ expect(
+ component.$el.querySelector('.folder-icon i.fa-caret-down').getAttribute('style'),
+ ).toContain('display: none');
+ expect(
+ component.$el.querySelector('.folder-icon i.fa-caret-right').getAttribute('style'),
+ ).not.toContain('display: none');
+ done();
+ });
+ });
+ });
+ });
+
+ it('should show children environments and a button to show all environments', (done) => {
+ setTimeout(() => {
+ // open folder
+ component.$el.querySelector('.folder-name').click();
+
+ Vue.nextTick(() => {
+ const folderInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify({
+ environments: [environment],
+ }), { status: 200 }));
+ };
+
+ Vue.http.interceptors.push(folderInterceptor);
+
+ // wait for next async request
+ setTimeout(() => {
+ expect(component.$el.querySelectorAll('.child-row').length).toEqual(1);
+ expect(component.$el.querySelector('td.text-center > a.btn').textContent).toContain('Show all');
+
+ Vue.http.interceptors = _.without(Vue.http.interceptors, folderInterceptor);
+ done();
+ });
+ });
+ });
+ });
+ });
});
diff --git a/spec/javascripts/environments/environments_store_spec.js b/spec/javascripts/environments/environments_store_spec.js
index 115d84b50f5..55181621e8d 100644
--- a/spec/javascripts/environments/environments_store_spec.js
+++ b/spec/javascripts/environments/environments_store_spec.js
@@ -1,58 +1,79 @@
import Store from '~/environments/stores/environments_store';
import { environmentsList, serverData } from './mock_data';
-(() => {
- describe('Store', () => {
- let store;
+describe('Store', () => {
+ let store;
- beforeEach(() => {
- store = new Store();
- });
+ beforeEach(() => {
+ store = new Store();
+ });
- it('should start with a blank state', () => {
- expect(store.state.environments.length).toEqual(0);
- expect(store.state.stoppedCounter).toEqual(0);
- expect(store.state.availableCounter).toEqual(0);
- expect(store.state.paginationInformation).toEqual({});
- });
+ it('should start with a blank state', () => {
+ expect(store.state.environments.length).toEqual(0);
+ expect(store.state.stoppedCounter).toEqual(0);
+ expect(store.state.availableCounter).toEqual(0);
+ expect(store.state.paginationInformation).toEqual({});
+ });
+
+ it('should store environments', () => {
+ store.storeEnvironments(serverData);
+ expect(store.state.environments.length).toEqual(serverData.length);
+ expect(store.state.environments[0]).toEqual(environmentsList[0]);
+ });
+
+ it('should store available count', () => {
+ store.storeAvailableCount(2);
+ expect(store.state.availableCounter).toEqual(2);
+ });
+
+ it('should store stopped count', () => {
+ store.storeStoppedCount(2);
+ expect(store.state.stoppedCounter).toEqual(2);
+ });
- it('should store environments', () => {
+ it('should store pagination information', () => {
+ const pagination = {
+ 'X-nExt-pAge': '2',
+ 'X-page': '1',
+ 'X-Per-Page': '1',
+ 'X-Prev-Page': '2',
+ 'X-TOTAL': '37',
+ 'X-Total-Pages': '2',
+ };
+
+ const expectedResult = {
+ perPage: 1,
+ page: 1,
+ total: 37,
+ totalPages: 2,
+ nextPage: 2,
+ previousPage: 2,
+ };
+
+ store.setPagination(pagination);
+ expect(store.state.paginationInformation).toEqual(expectedResult);
+ });
+
+ describe('toggleFolder', () => {
+ it('should toggle folder', () => {
store.storeEnvironments(serverData);
- expect(store.state.environments.length).toEqual(serverData.length);
- expect(store.state.environments[0]).toEqual(environmentsList[0]);
- });
- it('should store available count', () => {
- store.storeAvailableCount(2);
- expect(store.state.availableCounter).toEqual(2);
- });
+ store.toggleFolder(store.state.environments[1]);
+ expect(store.state.environments[1].isOpen).toEqual(true);
- it('should store stopped count', () => {
- store.storeStoppedCount(2);
- expect(store.state.stoppedCounter).toEqual(2);
+ store.toggleFolder(store.state.environments[1]);
+ expect(store.state.environments[1].isOpen).toEqual(false);
});
+ });
+
+ describe('setfolderContent', () => {
+ it('should store folder content', () => {
+ store.storeEnvironments(serverData);
+
+ store.setfolderContent(store.state.environments[1], serverData);
- it('should store pagination information', () => {
- const pagination = {
- 'X-nExt-pAge': '2',
- 'X-page': '1',
- 'X-Per-Page': '1',
- 'X-Prev-Page': '2',
- 'X-TOTAL': '37',
- 'X-Total-Pages': '2',
- };
-
- const expectedResult = {
- perPage: 1,
- page: 1,
- total: 37,
- totalPages: 2,
- nextPage: 2,
- previousPage: 2,
- };
-
- store.setPagination(pagination);
- expect(store.state.paginationInformation).toEqual(expectedResult);
+ expect(store.state.environments[1].children.length).toEqual(serverData.length);
+ expect(store.state.environments[1].children[0].isChildren).toEqual(true);
});
});
-})();
+});
diff --git a/spec/javascripts/environments/mock_data.js b/spec/javascripts/environments/mock_data.js
index 30861481cc5..15e11aa686b 100644
--- a/spec/javascripts/environments/mock_data.js
+++ b/spec/javascripts/environments/mock_data.js
@@ -84,3 +84,19 @@ export const environment = {
updated_at: '2017-01-31T10:53:46.894Z',
},
};
+
+export const folder = {
+ folderName: 'build',
+ size: 5,
+ id: 12,
+ name: 'build/update-README',
+ state: 'available',
+ external_url: null,
+ environment_type: 'build',
+ last_deployment: null,
+ 'stop_action?': false,
+ environment_path: '/root/review-app/environments/12',
+ stop_path: '/root/review-app/environments/12/stop',
+ created_at: '2017-02-01T19:42:18.400Z',
+ updated_at: '2017-02-01T19:42:18.400Z',
+};