Commit 0bee0186 authored by Filipa Lacerda's avatar Filipa Lacerda

Adds JS to toggle buttons [ci skip]

parent 5413bf04
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');
}
}
......@@ -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);
}
}
......@@ -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;
......
......@@ -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
......
......@@ -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'
......@@ -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')
......@@ -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
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', () => {
});
});
});
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