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 { ...@@ -17,4 +17,8 @@ export default class ClusterService {
installApplication(appId) { installApplication(appId) {
return axios.post(this.appInstallEndpointMap[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'; ...@@ -550,7 +550,15 @@ import ProjectVariables from './project_variables';
import(/* webpackChunkName: "clusters" */ './clusters/clusters_bundle') import(/* webpackChunkName: "clusters" */ './clusters/clusters_bundle')
.then(cluster => new cluster.default()) // eslint-disable-line new-cap .then(cluster => new cluster.default()) // eslint-disable-line new-cap
.catch((err) => { .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; throw err;
}); });
break; break;
......
...@@ -7,7 +7,7 @@ class Projects::ClustersController < Projects::ApplicationController ...@@ -7,7 +7,7 @@ class Projects::ClustersController < Projects::ApplicationController
before_action :authorize_admin_cluster!, only: [:destroy] before_action :authorize_admin_cluster!, only: [:destroy]
def index 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 end
def login def login
...@@ -64,10 +64,20 @@ class Projects::ClustersController < Projects::ApplicationController ...@@ -64,10 +64,20 @@ class Projects::ClustersController < Projects::ApplicationController
.execute(cluster) .execute(cluster)
if cluster.valid? if cluster.valid?
flash[:notice] = "Cluster was successfully updated." respond_to do |format|
redirect_to project_cluster_path(project, project.cluster) 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 else
render :show respond_to do |format|
format.json { head :bad_request }
format.html { render :show }
end
end end
end end
......
...@@ -5,6 +5,6 @@ ...@@ -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= 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 %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 .svg-content
= image_tag 'illustrations/labels.svg' = image_tag 'illustrations/labels.svg'
...@@ -21,8 +21,8 @@ ...@@ -21,8 +21,8 @@
%span.badge %span.badge
0 0
.nav-controls .nav-controls
= 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'
.ci-table .ci-table.js-clusters-list
.gl-responsive-table-row.table-row-header{ role: 'row' } .gl-responsive-table-row.table-row-header{ role: 'row' }
.table-section.section-30{ role: 'rowheader' } .table-section.section-30{ role: 'rowheader' }
= s_('ClusterIntegration|Cluster') = s_('ClusterIntegration|Cluster')
...@@ -31,27 +31,30 @@ ...@@ -31,27 +31,30 @@
.table-section.section-30{ role: 'rowheader' } .table-section.section-30{ role: 'rowheader' }
= s_('ClusterIntegration|Project namespace') = s_('ClusterIntegration|Project namespace')
.table-section.section-10{ role: 'rowheader' } .table-section.section-10{ role: 'rowheader' }
.gl-responsive-table-row - @clusters.each do |cluster|
.table-section.section-30 .gl-responsive-table-row
.table-mobile-header{ role: 'rowheader' } .table-section.section-30
= s_('ClusterIntegration|Cluster') .table-mobile-header{ role: 'rowheader' }= s_('ClusterIntegration|Cluster')
.table-mobile-content .table-mobile-content= cluster.name
Content goes here .table-section.section-30
.table-section.section-30 .table-mobile-header{ role: 'rowheader' }
.table-mobile-header{ role: 'rowheader' } = s_('ClusterIntegration|Environment pattern')
= s_('ClusterIntegration|Environment pattern') .table-mobile-content
.table-mobile-content Content goes here
Content goes here .table-section.section-30
.table-section.section-30 .table-mobile-header{ role: 'rowheader' }
.table-mobile-header{ role: 'rowheader' } = s_('ClusterIntegration|Project namespace')
= s_('ClusterIntegration|Project namespace') .table-mobile-content
.table-mobile-content Content goes here
Content goes here .table-section.section-10
.table-section.section-10 .table-mobile-header{ role: 'rowheader' }
.table-mobile-header{ role: 'rowheader' } .table-mobile-content
.table-mobile-content %button{ type: 'button',
%button{ type: 'button', class: "js-toggle-cluster-list project-feature-toggle #{'checked' unless !cluster.enabled?} #{'disabled' unless can?(current_user, :update_cluster, cluster)}",
class: "js-toggle-cluster project-feature-toggle", 'aria-label': s_('ClusterIntegration|Toggle Cluster'),
'aria-label': s_('ClusterIntegration|Toggle Cluster'), disabled: !can?(current_user, :update_cluster, cluster),
data: { 'enabled-text': 'Enabled', 'disabled-text': 'Disabled' } } 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 ...@@ -94,6 +94,33 @@ feature 'Clusters', :js do
visit project_clusters_path(project) visit project_clusters_path(project)
end 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 context 'when user clicks on a cluster' do
before do before do
# TODO: Replace with Click on cluster after frontend implements list # TODO: Replace with Click on cluster after frontend implements list
...@@ -216,4 +243,6 @@ feature 'Clusters', :js do ...@@ -216,4 +243,6 @@ feature 'Clusters', :js do
expect(page).to have_css('.signin-with-google') expect(page).to have_css('.signin-with-google')
end end
end end
context
end 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