Merge "Allow inline name editing"
This commit is contained in:
@@ -21,15 +21,17 @@ from django.template import defaultfilters
|
|||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
|
from horizon import forms
|
||||||
from horizon import messages
|
from horizon import messages
|
||||||
from horizon import tables
|
from horizon import tables
|
||||||
|
|
||||||
|
from muranodashboard import api as api_utils
|
||||||
|
from muranodashboard.api import packages as pkg_api
|
||||||
from muranodashboard.catalog import views as catalog_views
|
from muranodashboard.catalog import views as catalog_views
|
||||||
from muranodashboard.environments import api
|
from muranodashboard.environments import api
|
||||||
from muranodashboard.environments import consts
|
from muranodashboard.environments import consts
|
||||||
|
|
||||||
from muranodashboard import api as api_utils
|
from muranoclient.common import exceptions as exc
|
||||||
from muranodashboard.api import packages as pkg_api
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -135,21 +137,6 @@ class AbandonEnvironment(tables.DeleteAction):
|
|||||||
exceptions.handle(request, msg, redirect=redirect)
|
exceptions.handle(request, msg, redirect=redirect)
|
||||||
|
|
||||||
|
|
||||||
class EditEnvironment(tables.LinkAction):
|
|
||||||
name = 'edit'
|
|
||||||
verbose_name = _('Edit Environment')
|
|
||||||
url = 'horizon:murano:environments:update_environment'
|
|
||||||
classes = ('ajax-modal', 'btn-edit')
|
|
||||||
icon = 'edit'
|
|
||||||
|
|
||||||
def allowed(self, request, environment):
|
|
||||||
"""Allow edit environment only when status not deploying and deleting.
|
|
||||||
"""
|
|
||||||
status = getattr(environment, 'status', None)
|
|
||||||
return status not in [consts.STATUS_ID_DEPLOYING,
|
|
||||||
consts.STATUS_ID_DELETING]
|
|
||||||
|
|
||||||
|
|
||||||
class DeleteService(tables.DeleteAction):
|
class DeleteService(tables.DeleteAction):
|
||||||
data_type_singular = _('Component')
|
data_type_singular = _('Component')
|
||||||
data_type_plural = _('Components')
|
data_type_plural = _('Components')
|
||||||
@@ -270,10 +257,36 @@ class UpdateServiceRow(tables.Row):
|
|||||||
return api.service_get(request, environment_id, service_id)
|
return api.service_get(request, environment_id, service_id)
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateName(tables.UpdateAction):
|
||||||
|
def update_cell(self, request, datum, obj_id, cell_name, new_cell_value):
|
||||||
|
try:
|
||||||
|
mc = api_utils.muranoclient(request)
|
||||||
|
mc.environments.update(datum.id, name=new_cell_value)
|
||||||
|
except exc.HTTPConflict:
|
||||||
|
message = _("This name is already taken.")
|
||||||
|
messages.warning(request, message)
|
||||||
|
LOG.warning(_("Couldn't update environment. Reason: ") + message)
|
||||||
|
|
||||||
|
# FIXME(kzaitsev): There is a bug in horizon and inline error
|
||||||
|
# icons are missing. This means, that if we return 400 here, by
|
||||||
|
# raising django.core.exceptions.ValidationError(message) the UI
|
||||||
|
# will break a little. Until the bug is fixed this will raise 500
|
||||||
|
# bug link: https://bugs.launchpad.net/horizon/+bug/1359399
|
||||||
|
# Alternatively this could somehow raise 409, which would result
|
||||||
|
# in the same behaviour.
|
||||||
|
raise ValueError(message)
|
||||||
|
except Exception:
|
||||||
|
exceptions.handle(request, ignore=True)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class EnvironmentsTable(tables.DataTable):
|
class EnvironmentsTable(tables.DataTable):
|
||||||
name = tables.Column('name',
|
name = tables.Column('name',
|
||||||
link='horizon:murano:environments:services',
|
link='horizon:murano:environments:services',
|
||||||
verbose_name=_('Name'))
|
verbose_name=_('Name'),
|
||||||
|
form_field=forms.CharField(),
|
||||||
|
update_action=UpdateName)
|
||||||
|
|
||||||
status = tables.Column('status',
|
status = tables.Column('status',
|
||||||
verbose_name=_('Status'),
|
verbose_name=_('Status'),
|
||||||
@@ -290,7 +303,7 @@ class EnvironmentsTable(tables.DataTable):
|
|||||||
no_data_message = _('NO ENVIRONMENTS')
|
no_data_message = _('NO ENVIRONMENTS')
|
||||||
table_actions = (CreateEnvironment,)
|
table_actions = (CreateEnvironment,)
|
||||||
row_actions = (ShowEnvironmentServices, DeployEnvironment,
|
row_actions = (ShowEnvironmentServices, DeployEnvironment,
|
||||||
EditEnvironment, DeleteEnvironment, AbandonEnvironment)
|
DeleteEnvironment, AbandonEnvironment)
|
||||||
multi_select = False
|
multi_select = False
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,3 +1,35 @@
|
|||||||
|
table#murano div.table_cell_wrapper div.inline-edit-form input {
|
||||||
|
/*
|
||||||
|
* overrides from form-control, to make inline edit consistent
|
||||||
|
* with the rest of UI
|
||||||
|
*/
|
||||||
|
display: block;
|
||||||
|
height: 32px;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.42857;
|
||||||
|
color: #555;
|
||||||
|
background-color: #fff;
|
||||||
|
background-image: none;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||||
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||||
|
-webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
|
||||||
|
-o-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
|
||||||
|
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
|
||||||
|
|
||||||
|
/* Additional styles, not present in form-control */
|
||||||
|
margin: 7px 7px 0 7px;
|
||||||
|
width: calc(100% - 14px);
|
||||||
|
}
|
||||||
|
|
||||||
|
table#murano div.table_cell_wrapper div.inline-edit-form input:focus {
|
||||||
|
border-color: #66afe9;
|
||||||
|
outline: 0;
|
||||||
|
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
|
||||||
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
.table_header.catalog {
|
.table_header.catalog {
|
||||||
border-bottom: 2px solid #e5e5e5;
|
border-bottom: 2px solid #e5e5e5;
|
||||||
padding-bottom:5px;
|
padding-bottom:5px;
|
||||||
|
@@ -12,11 +12,11 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block css %}
|
{% block css %}
|
||||||
{% include "_stylesheets.html" %}
|
{{ block.super }}
|
||||||
<link href='{{ STATIC_URL }}muranodashboard/css/catalog.css' type='text/css' media='screen' rel='stylesheet' />
|
<link href='{{ STATIC_URL }}muranodashboard/css/catalog.css' type='text/css' media='screen' rel='stylesheet' />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
{% include 'horizon/_scripts.html' %}
|
{% include 'horizon/_scripts.html' %}
|
||||||
<script src="{% static 'muranodashboard/js/environments-in-place.js' %}"></script>
|
<script src="{% static 'muranodashboard/js/environments-in-place.js' %}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@@ -23,6 +23,7 @@ from keystoneclient.v2_0 import client as ksclient
|
|||||||
from muranoclient import client as mclient
|
from muranoclient import client as mclient
|
||||||
from selenium.common import exceptions as exc
|
from selenium.common import exceptions as exc
|
||||||
from selenium import webdriver
|
from selenium import webdriver
|
||||||
|
from selenium.webdriver.common.action_chains import ActionChains
|
||||||
import selenium.webdriver.common.by as by
|
import selenium.webdriver.common.by as by
|
||||||
from selenium.webdriver.support import expected_conditions as EC
|
from selenium.webdriver.support import expected_conditions as EC
|
||||||
from selenium.webdriver.support import ui
|
from selenium.webdriver.support import ui
|
||||||
@@ -195,10 +196,29 @@ class UITestCase(BaseDeps):
|
|||||||
self.wait_for_alert_message()
|
self.wait_for_alert_message()
|
||||||
|
|
||||||
def edit_environment(self, old_name, new_name):
|
def edit_environment(self, old_name, new_name):
|
||||||
self.select_action_for_environment(old_name, 'edit')
|
el_td = self.driver.find_element_by_css_selector(
|
||||||
self.fill_field(by.By.ID, 'id_name', new_name)
|
'tr[data-display="{0}"] td:first-of-type'.format(old_name))
|
||||||
self.driver.find_element_by_xpath(consts.InputSubmit).click()
|
el_pencil = el_td.find_element_by_css_selector(
|
||||||
self.wait_for_alert_message()
|
'button.ajax-inline-edit')
|
||||||
|
|
||||||
|
# hover to make pencil visible
|
||||||
|
hover = ActionChains(self.driver).move_to_element(el_td)
|
||||||
|
hover.perform()
|
||||||
|
el_pencil.click()
|
||||||
|
|
||||||
|
# fill in inline input
|
||||||
|
el_inline_input = self.driver.find_element_by_css_selector(
|
||||||
|
'tr[data-display="{0}"] '.format(old_name) +
|
||||||
|
'td:first-of-type .inline-edit-form input')
|
||||||
|
el_inline_input.clear()
|
||||||
|
el_inline_input.send_keys(new_name)
|
||||||
|
|
||||||
|
# click submit
|
||||||
|
el_submit = self.driver.find_element_by_css_selector(
|
||||||
|
'tr[data-display="{0}"] '.format(old_name) +
|
||||||
|
'td:first-of-type .inline-edit-actions button[type="submit"]')
|
||||||
|
el_submit.click()
|
||||||
|
# there is no alert message
|
||||||
|
|
||||||
def select_action_for_environment(self, env_name, action):
|
def select_action_for_environment(self, env_name, action):
|
||||||
element_id = self.get_element_id(env_name)
|
element_id = self.get_element_id(env_name)
|
||||||
|
@@ -74,9 +74,10 @@ class TestSuiteEnvironment(base.ApplicationTestCase):
|
|||||||
self.go_to_submenu('Environments')
|
self.go_to_submenu('Environments')
|
||||||
self.create_environment('test_edit_env')
|
self.create_environment('test_edit_env')
|
||||||
self.go_to_submenu('Environments')
|
self.go_to_submenu('Environments')
|
||||||
self.driver.find_element_by_link_text('test_edit_env')
|
|
||||||
|
|
||||||
self.edit_environment(old_name='test_edit_env', new_name='edited_env')
|
self.edit_environment(old_name='test_edit_env', new_name='edited_env')
|
||||||
|
|
||||||
|
self.go_to_submenu('Environments')
|
||||||
self.check_element_on_page(by.By.LINK_TEXT, 'edited_env')
|
self.check_element_on_page(by.By.LINK_TEXT, 'edited_env')
|
||||||
self.check_element_not_on_page(by.By.LINK_TEXT, 'test_edit_env')
|
self.check_element_not_on_page(by.By.LINK_TEXT, 'test_edit_env')
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user