Adds JS to toggle buttons [ci skip]

This commit is contained in:
Filipa Lacerda 2017-11-24 13:43:00 +00:00
parent 5413bf0426
commit 0bee018631
No known key found for this signature in database
GPG Key ID: 9CA3FDE4D1E2F1C8
8 changed files with 178 additions and 31 deletions

View File

@ -0,0 +1,62 @@
import Flash from '../flash';
import { s__ } from '../locale';
import ClustersService from './services/clusters_service';
/**
* Handles toggle buttons in the cluster's table.
*
* When the user clicks the toggle button for each cluster, it:
* - toggles the button
* - shows a loding and disabled state
* - Makes a put request to the given endpoint
* Once we receive the response, either:
* 1) Show updated status in case of successfull response
* 2) Show initial status in case of failed response
*/
export default class ClusterTable {
constructor() {
this.container = '.js-clusters-list';
document.querySelectorAll(`${this.container} .js-toggle-cluster-list`).forEach(button => button.addEventListener('click', e => ClusterTable.updateCluster(e)));
}
removeListeners() {
document.querySelectorAll(`${this.container} .js-toggle-cluster-list`).forEach(button => button.removeEventListener('click'));
}
static updateCluster(e) {
const toggleButton = e.currentTarget;
const value = toggleButton.classList.contains('checked').toString();
const endpoint = toggleButton.getAttribute('data-endpoint');
ClusterTable.toggleValue(toggleButton);
ClusterTable.toggleLoadingButton(toggleButton);
ClustersService.updateCluster(endpoint, { cluster: { enabled: value } })
.then(() => {
ClusterTable.toggleLoadingButton(toggleButton);
})
.catch(() => {
ClusterTable.toggleLoadingButton(toggleButton);
ClusterTable.toggleValue(toggleButton);
Flash(s__('ClusterIntegration|Something went wrong on our end.'));
});
}
/**
* Toggles loading and disabled classes.
* @param {HTMLElement} button
*/
static toggleLoadingButton(button) {
button.setAttribute('disabled', button.getAttribute('disabled'));
button.classList.toggle('disabled');
button.classList.toggle('loading');
}
/**
* Toggles checked class for the given button
* @param {HTMLElement} button
*/
static toggleValue(button) {
button.classList.toggle('checked');
}
}

View File

@ -17,4 +17,8 @@ export default class ClusterService {
installApplication(appId) {
return axios.post(this.appInstallEndpointMap[appId]);
}
static updateCluster(endpoint, data) {
return axios.put(endpoint, data);
}
}

View File

@ -550,7 +550,15 @@ import ProjectVariables from './project_variables';
import(/* webpackChunkName: "clusters" */ './clusters/clusters_bundle')
.then(cluster => new cluster.default()) // eslint-disable-line new-cap
.catch((err) => {
Flash(s__('ClusterIntegration|Problem setting up the cluster JavaScript'));
Flash(s__('ClusterIntegration|Problem setting up the cluster'));
throw err;
});
break;
case 'projects:clusters:index':
import(/* webpackChunkName: "clusters_index" */ './clusters/clusters_index')
.then(clusterIndex => new clusterIndex.default()) // eslint-disable-line new-cap
.catch((err) => {
Flash(s__('ClusterIntegration|Problem setting up the clusters list'));
throw err;
});
break;

View File

@ -7,7 +7,7 @@ class Projects::ClustersController < Projects::ApplicationController
before_action :authorize_admin_cluster!, only: [:destroy]
def index
@clusters ||= project.clusters.map { |cluster| cluster.present(current_user: current_user) }
@clusters ||= project.clusters.page(params[:page]).per(20).map { |cluster| cluster.present(current_user: current_user) }
end
def login
@ -64,10 +64,20 @@ class Projects::ClustersController < Projects::ApplicationController
.execute(cluster)
if cluster.valid?
flash[:notice] = "Cluster was successfully updated."
redirect_to project_cluster_path(project, project.cluster)
respond_to do |format|
format.json do
head :no_content
end
format.html do
flash[:notice] = "Cluster was successfully updated."
redirect_to project_cluster_path(project, project.cluster)
end
end
else
render :show
respond_to do |format|
format.json { head :bad_request }
format.html { render :show }
end
end
end

View File

@ -5,6 +5,6 @@
%p= s_('ClusterIntegration|Clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}').html_safe % { link_to_help_page: link_to_help_page}
%p
= link_to s_('ClusterIntegration|Add cluster'), '', class: 'btn btn-success', title: 'Add cluster'
= link_to s_('ClusterIntegration|Add cluster'), new_project_cluster_path(@project), class: 'btn btn-success', title: 'Add cluster'
.svg-content
= image_tag 'illustrations/labels.svg'

View File

@ -21,8 +21,8 @@
%span.badge
0
.nav-controls
= link_to s_('ClusterIntegration|Add cluster'), '', class: 'btn btn-success', title: 'Add cluster'
.ci-table
= link_to s_('ClusterIntegration|Add cluster'), new_project_cluster_path(@project), class: 'btn btn-success', title: 'Add cluster'
.ci-table.js-clusters-list
.gl-responsive-table-row.table-row-header{ role: 'row' }
.table-section.section-30{ role: 'rowheader' }
= s_('ClusterIntegration|Cluster')
@ -31,27 +31,30 @@
.table-section.section-30{ role: 'rowheader' }
= s_('ClusterIntegration|Project namespace')
.table-section.section-10{ role: 'rowheader' }
.gl-responsive-table-row
.table-section.section-30
.table-mobile-header{ role: 'rowheader' }
= s_('ClusterIntegration|Cluster')
.table-mobile-content
Content goes here
.table-section.section-30
.table-mobile-header{ role: 'rowheader' }
= s_('ClusterIntegration|Environment pattern')
.table-mobile-content
Content goes here
.table-section.section-30
.table-mobile-header{ role: 'rowheader' }
= s_('ClusterIntegration|Project namespace')
.table-mobile-content
Content goes here
.table-section.section-10
.table-mobile-header{ role: 'rowheader' }
.table-mobile-content
%button{ type: 'button',
class: "js-toggle-cluster project-feature-toggle",
'aria-label': s_('ClusterIntegration|Toggle Cluster'),
data: { 'enabled-text': 'Enabled', 'disabled-text': 'Disabled' } }
- @clusters.each do |cluster|
.gl-responsive-table-row
.table-section.section-30
.table-mobile-header{ role: 'rowheader' }= s_('ClusterIntegration|Cluster')
.table-mobile-content= cluster.name
.table-section.section-30
.table-mobile-header{ role: 'rowheader' }
= s_('ClusterIntegration|Environment pattern')
.table-mobile-content
Content goes here
.table-section.section-30
.table-mobile-header{ role: 'rowheader' }
= s_('ClusterIntegration|Project namespace')
.table-mobile-content
Content goes here
.table-section.section-10
.table-mobile-header{ role: 'rowheader' }
.table-mobile-content
%button{ type: 'button',
class: "js-toggle-cluster-list project-feature-toggle #{'checked' unless !cluster.enabled?} #{'disabled' unless can?(current_user, :update_cluster, cluster)}",
'aria-label': s_('ClusterIntegration|Toggle Cluster'),
disabled: !can?(current_user, :update_cluster, cluster),
data: { 'enabled-text': 'Enabled',
'disabled-text': 'Disabled',
endpoint: namespace_project_cluster_path(@project.namespace, @project, cluster, format: :json) } }
= icon('loading', class: 'hidden')

View File

@ -94,6 +94,33 @@ feature 'Clusters', :js do
visit project_clusters_path(project)
end
it 'user sees a table with one cluster' do
end
it 'user sees a disabled add cluster button ' do
end
it 'user sees navigation tabs' do
end
context 'update cluster' do
it 'user can update cluster' do
end
context 'with sucessfull request' do
it 'user sees updated cluster' do
end
end
context 'with failed request' do
it 'user sees not update cluster and error message' do
end
end
end
context 'when user clicks on a cluster' do
before do
# TODO: Replace with Click on cluster after frontend implements list
@ -216,4 +243,6 @@ feature 'Clusters', :js do
expect(page).to have_css('.signin-with-google')
end
end
context
end

View File

@ -0,0 +1,31 @@
import ClusterTable from '~/clusters/clusters_index';
describe('Clusters table', () => {
let ClustersClass;
beforeEach(() => {
ClustersClass = new ClusterTable();
});
afterEach(() => {
ClustersClass.removeListeners();
});
describe('update cluster', () => {
it('renders a toggle button', () => {
});
it('renders loading state while request is made', () => {
});
it('shows updated state after sucessfull request', () => {
});
it('shows inital state after failed request', () => {
});
});
});