Commit 2e9fd5db authored by jeromecambon's avatar jeromecambon Committed by benjaminParisel

feat(view edit widget): view a Bonita widget or edit a Custom widget (#2680)

Covers [UID-63](https://bonitasoft.atlassian.net/browse/UID-63)
parent df8bc20b
......@@ -6,7 +6,7 @@
</th>
<th class="col-xs-4 Property-actions" colspan="2">
<button class="btn btn-default btn-data--help" ng-click="vm.openHelp('widget')" title="{{'Open help' | translate}}"><i class="fa fa-question-circle"></i></button>
<button id="addasset" title="Add a new asset" class="btn btn-bonita-primary" ng-click="vm.openAssetPopup()">
<button id="addasset" title="Add a new asset" class="btn btn-bonita-primary" ng-disabled="!widget.custom" ng-click="vm.openAssetPopup()">
<i class="fa fa-plus"></i>
<span translate>Add</span>
</button>
......@@ -23,8 +23,8 @@
<tbody>
<tr ng-repeat="asset in vm.component.assets | orderBy:'order' as results">
<td>
<a ng-click="vm.decrementOrderAsset(asset)" ng-if="!$first" title="{{ 'Move asset up' | translate }}"><i class="fa fa-arrow-up asset-ordering-action clickable"></i></a>
<a ng-click="vm.incrementOrderAsset(asset)" ng-if="!$last" title="{{ 'Move asset down' | translate }}"><i class="fa fa-arrow-down asset-ordering-action clickable"></i></a>
<a ng-click="vm.decrementOrderAsset(asset)" ng-if="!$first && widget.custom" title="{{ 'Move asset up' | translate }}"><i class="fa fa-arrow-up asset-ordering-action clickable"></i></a>
<a ng-click="vm.incrementOrderAsset(asset)" ng-if="!$last && widget.custom" title="{{ 'Move asset down' | translate }}"><i class="fa fa-arrow-down asset-ordering-action clickable"></i></a>
</td>
<td>{{asset.name}}</td>
<td>{{asset.type | assetType}}</td>
......@@ -36,11 +36,11 @@
ng-if="vm.isViewable(asset)" ng-click="vm.openAssetPreviewPopup(asset)">
<i class="fa fa-search"></i>
</button>
<button class="btn btn-bonita-default" ng-click="vm.openAssetEditPopup(asset)"
<button id="editAsset" class="btn btn-bonita-default" ng-disabled="!widget.custom" ng-click="vm.openAssetEditPopup(asset)"
title="{{ 'Update asset' | translate }}" ng-if="vm.isEditable(asset)">
<i class="fa fa-pencil"></i>
</button>
<button class="btn btn-bonita-default" ng-click="vm.delete(asset)" title="{{ 'Delete asset' | translate }}">
<button id="deleteAsset" class="btn btn-bonita-default" ng-disabled="!widget.custom" ng-click="vm.delete(asset)" title="{{ 'Delete asset' | translate }}">
<i class="fa fa-trash"></i>
</button>
</td>
......
......@@ -8,14 +8,15 @@
<i class="ui-icon ui-designer"></i>
</a>
<span class="EditorHeader-brand" translate>
Custom widget editor
widget editor
</span>
<h3 class="EditorHeader-name EditorHeader-name--custom">
<identicon name="{{widget.id}}" size="30" foreground-color="[203, 213, 225, 255]" background-color="[0,0,0,0]"></identicon>
{{widget.name}}
</h3>
<div class="EditorHeader-actions">
<div class="btn-group" uib-dropdown>
<button ng-if="!widget.custom" id="saveas-bonita" type="button" class="btn btn-bonita-primary" ng-click="saveAs(widget)" translate>Save as...</button>
<div ng-if="widget.custom" class="btn-group" uib-dropdown>
<button id="save" type="button" class="btn btn-bonita-primary" ng-click="save()" ng-disabled="!isPageDirty(widget)" translate>Save</button>
<button type="button" class="btn btn-bonita-primary uib-dropdown-toggle" uib-dropdown-toggle>
<span class="fa fa-caret-down"></span>
......@@ -27,6 +28,7 @@
</div>
<button type="button"
id="export"
ng-disabled="!widget.custom"
class="btn btn-bonita-primary"
ng-click="saveAndExport()"
title="{{'Export'|translate}}">
......@@ -51,15 +53,15 @@
<translate>Description</translate>
<i class="fa fa-info-circle CustomEditor-helper" tooltip-placement="bottom" uib-tooltip="{{'This description will be displayed as a tooltip in the palette' | translate }}" tooltip-append-to-body="true"></i>
</h4>
<textarea class="form-control textarea--widget" ng-attr-placeholder="{{ 'Widget description' | translate }}" ng-model="widget.description"></textarea>
<textarea class="form-control textarea--widget" ng-disabled="!widget.custom" ng-attr-placeholder="{{ 'Widget description' | translate }}" ng-model="widget.description"></textarea>
</div>
<div>
<h4 class="CustomEditor-section" translate>Template</h4>
<ace-editor class="ace-editor--widget" mode="html" ng-model="widget.template" id="template"></ace-editor>
<ace-editor id="template" class="ace-editor--widget" ng-class="{'ace_editor-readonly': !widget.custom}" mode="html" readonly="{{!widget.custom}}" ng-model="widget.template"></ace-editor>
</div>
<div>
<h4 class="CustomEditor-section" translate>Controller</h4>
<ace-editor class="ace-editor--widget" mode="javascript" ng-model="widget.controller" id="controller"></ace-editor>
<ace-editor id="controller" class="ace-editor--widget" ng-class="{'ace_editor-readonly': !widget.custom}" mode="javascript" readonly="{{!widget.custom}}" ng-model="widget.controller"></ace-editor>
</div>
</form>
</div>
......@@ -72,7 +74,7 @@
<translate>Required angular modules</translate>
<i class="fa fa-info-circle CustomEditor-helper" uib-tooltip="{{'Extra angular modules needed by this custom widget (comma separated)' | translate }}" tooltip-append-to-body="true"></i>
</h4>
<input type="text" class="form-control" id="requiredModules" ng-model="widget.requiredModules" ng-list>
<input type="text" class="form-control" id="requiredModules" ng-disabled="!widget.custom" ng-model="widget.requiredModules" ng-list>
</div>
<table class="table table-striped">
......@@ -81,7 +83,7 @@
<h4 class="CustomEditor-section" translate>Properties</h4>
</th>
<th class="col-xs-2 Property-actions">
<button id="create" title="Create a new property" class="btn btn-bonita-primary" ng-click="createOrUpdate()">
<button id="create" title="Create a new property" class="btn btn-bonita-primary" ng-disabled="!widget.custom" ng-click="createOrUpdate()">
<i class="fa fa-plus"></i>
<span translate>Create</span>
</button>
......@@ -98,8 +100,8 @@
<div ng-if="property.bond !== 'variable'"><small translate>Default value</small>: {{ property.defaultValue }}</div>
</td>
<td class="col-xs-2 Property-actions">
<button class="btn btn-bonita-default" title="Update property" ng-click="createOrUpdate(property)"><i class="fa fa-pencil"></i></button>
<button class="btn btn-bonita-default" title="Delete property" ng-click="deleteParam(property)"><i class="fa fa-trash"></i></button>
<button id="editProperty" class="btn btn-bonita-default" title="Update property" ng-disabled="!widget.custom" ng-click="createOrUpdate(property)"><i class="fa fa-pencil"></i></button>
<button id="deleteProperty" class="btn btn-bonita-default" title="Delete property" ng-disabled="!widget.custom" ng-click="deleteParam(property)"><i class="fa fa-trash"></i></button>
</td>
</tr>
</tbody>
......
......@@ -5,8 +5,11 @@
<div class="modal-body">
<uib-tabset class="tabs-left" active="tabContainer.activeTab">
<uib-tab id="help-general" heading="{{ 'General' | translate }}" index="'general'">
<p translate>If the standard widgets do not meet your needs, you can create a custom widget to match exactly what you want to. After creation, a custom widget is available in the palette for designing pages, forms or fragments.</p>
<p translate>Custom widget implementation is based on the AngularJS framework.</p>
<p translate>This page allows you to view a standard widget, or edit a custom widget.</p>
<p translate>If the standard widget does not meet your needs, you can create a custom widget to match exactly what you want to.
To do this, clone the standard widget by saving it with a different name, and modify it.
After creation, the custom widget is available in the palette for designing pages, forms or fragments.</p>
<p translate>Widget implementation is based on the AngularJS framework.</p>
<p translate>For more information, see the AngularJS guide on
<a href="https://docs.angularjs.org/guide/templates">templates</a> and
<a href="https://docs.angularjs.org/guide/controller">controllers</a>.</p>
......@@ -15,24 +18,25 @@
</uib-tab>
<uib-tab id="help-template" heading="{{ 'Template' | translate }}" index="'template'">
<p translate>The custom widget HTML template is defined here.</p>
<p translate>The widget HTML template is defined here.</p>
<p translate>You can use standard HTML tags and AngularJS built-in directives, scope and interpolation system.</p>
<p translate>Custom widget properties defined on the right can be used as variables in a templates with properties.newProperty.</p>
<p translate>Widget properties defined on the right can be used as variables in a template with properties.newProperty.</p>
<p translate>Functions exposed in the controller can be used with ctrl.newFunction().</p>
<p translate>You can use the <i>environment</i> property injected in the scope when inside the whiteboard editor. It allows to create a mockup display for the whiteboard as the real use data will not be available.</p>
<p translate>You can use the <i>environment</i> property injected in the scope when inside the whiteboard editor.
It allows to create a mockup display for the whiteboard as the real use data will not be available.</p>
</uib-tab>
<uib-tab id="help-controller" heading="{{ 'Controller' | translate }}" index="'controller'">
<p translate>The controller is a JavaScript function that augments the AngularJS scope and exposes functions that can be used in the custom widget template.</p>
<p translate>The controller is a JavaScript function that augments the AngularJS scope and exposes functions that can be used in the widget template.</p>
<p translate>Custom widget properties defined on the right can be used as variables in a controller with $scope.properties.</p>
<p translate>Widget properties defined on the right can be used as variables in a controller with $scope.properties.</p>
<p translate>To use AngularJS standard services, you must declare them in the main function arguments.</p>
<p translate>You can leave the controller empty if you do not need it.</p>
<p translate>Controller can be left empty if not needed.</p>
</uib-tab>
<uid-l10n-help-tab></uid-l10n-help-tab>
<uib-tab id="help-migration" heading="{{ 'Migration' | translate }}" index="'migration'">
......
......@@ -6,13 +6,21 @@
popover-trigger="none" popover-is-open="widgetHelpPopover.isOpen" popover-placement="left-top" popover-class="PropertyPanel-help-popover">
<i class="fa fa-question-circle"></i>
</button>
<button ng-if="currentComponent.$$widget.type !== 'container'"
ng-click="saveAndEditCustomWidget(currentComponent.$$widget.id)" class="btn btn-xs btn-bonita-edit-widget PropertyPanel-edit-btn"
id="edit-widget"
tooltip-placement="top-right" uib-tooltip="{{ 'Edit widget' | translate }}"
tooltip-append-to-body="true">
<i class="fa fa-pencil"></i>
</button>
<div id="action" class="btn-group PropertyPanel-widget-action-btn" uib-dropdown>
<button ng-if="currentComponent.$$widget.type !== 'container' && currentComponent.type !== 'fragment'" id="widgetActionButton" type="button" class="btn btn-xs btn-bonita-action-widget" uib-dropdown-toggle
tooltip-placement="top-right" uib-tooltip="{{ 'Widget actions' | translate }}"
tooltip-append-to-body="true">
<span class="fa fa-ellipsis-v"/>
</button>
<ul class="dropdown-menu dropdown-menu-right PropertyPanel-widget-action-menu" role="menu" aria-labelledby="typeButton">
<li><a ng-if="!currentComponent.$$widget.custom" id="viewAction" class="clickable PropertyPanel-widget-action-item"
ng-click="saveAndEditCustomWidget(currentComponent.$$widget.id)" translate>View...</a></li>
<li><a ng-if="currentComponent.$$widget.custom" id="editAction" class="clickable PropertyPanel-widget-action-item"
ng-click="saveAndEditCustomWidget(currentComponent.$$widget.id)" translate>Edit...</a></li>
<li><a id="switchAction" class="clickable PropertyPanel-widget-action-item" ng-click="" translate>Switch...</a></li>
</ul>
</div>
<h3 class="PropertyPanel-title">
{{ currentComponent.$$widget.name }}
</h3>
......
......@@ -123,6 +123,16 @@ PropertyPanel-help-popover-body {
margin-right:7px;
}
.PropertyPanel-widget-action-btn {
margin-top: 17px;
margin-bottom: 15px;
font-size: 14px;
float: right;
}
.PropertyPanel-widget-action-menu {
min-width: 120px;
}
.PropertyPanel-content {
flex: 1;
overflow-x: hidden;
......
......@@ -116,7 +116,7 @@
}
}
.btn-bonita-edit-widget {
.btn-bonita-action-widget {
.button-variant(@btn-bonita-edit-widget-color, @btn-bonita-edit-widget-bg, @btn-bonita-edit-widget-border);
&:hover {
......@@ -170,6 +170,7 @@
@color-custom-editor-section: #E21938;
@color-custom-editor-helper: #0B4361;
@color-custom-editor-widget-name: #CBD5E1;
@color-custom-editor-disable-background: #EEEEEE;
/**
......
.ace_editor.ace-editor--widget {
height: 300px;
}
.ace_editor-readonly .ace_content{
cursor: not-allowed;
background-color: @color-custom-editor-disable-background;
}
.textarea--widget {
resize:vertical;
......
......@@ -71,7 +71,7 @@ describe('custom widget editor', function() {
expect(properties).toEqual(['qualifier', 'verb']);
});
it('should allow to add a property', function() {
it('if custom only, should allow to add a property', function() {
$('#create').click();
$('#name').sendKeys('newProperty');
......@@ -94,6 +94,10 @@ describe('custom widget editor', function() {
expect($('.modal-body #help').getAttribute('value')).toBe('Tooltip for new property');
expect($('.modal-body #caption').getAttribute('value')).toBe('Caption for new property');
expect($('.modal-body #default').getAttribute('value')).toBe('Default value');
// Standard widget: create button should be disabled
widgetEditor = WidgetEditor.get('pbInput');
expect(element(by.css('#create:disabled')).isPresent()).toBeTruthy();
});
it('should allow to update a property', function() {
......@@ -123,6 +127,11 @@ describe('custom widget editor', function() {
expect(updated.all(by.tagName('div')).get(3).getText()).toBe('Type: choice');
expect(updated.all(by.tagName('div')).get(4).getText()).toBe('Choices: red, green, blue');
expect(updated.all(by.tagName('div')).get(5).getText()).toBe('Default value: red');
// Standard widget: update property should be disabled
widgetEditor = WidgetEditor.get('pbInput');
property = element.all(by.repeater('property in widget.properties')).first();
expect(property.element(by.css('#editProperty:disabled')).isPresent()).toBeTruthy();
});
it('should allow to choose property type only with some bond type while creating/updating a property', function() {
......@@ -171,6 +180,11 @@ describe('custom widget editor', function() {
var properties = getPropertyNamesInList();
expect(properties).not.toContain(firstParamName);
// Standard widget: delete property should be disabled
widgetEditor = WidgetEditor.get('pbInput');
var property = element.all(by.repeater('property in widget.properties')).first();
expect(property.element(by.css('#deleteProperty:disabled')).isPresent()).toBeTruthy();
});
it('should allow to edit a widget template and controller', function() {
......@@ -187,6 +201,11 @@ describe('custom widget editor', function() {
// should go back to root when saved
expect(browser.getCurrentUrl()).toMatch(/.*#\//);
// Standard widget: edit template and controller should be disabled
widgetEditor = WidgetEditor.get('pbInput');
expect(element(by.css('#template:read-only')).isPresent()).toBeTruthy();
expect(element(by.css('#controller:read-only')).isPresent()).toBeTruthy();
});
it('should save a widget as', function() {
......@@ -243,6 +262,13 @@ describe('custom widget editor', function() {
expect(localEditableAsset.element(by.css('button i.fa-search')).isPresent()).toBeFalsy();
expect(localEditableAsset.element(by.css('button i.fa-pencil')).isPresent()).toBeTruthy();
expect(localEditableAsset.element(by.css('button i.fa-trash')).isPresent()).toBeTruthy();
// Standard widget: edit/delete should be disabled
widgetEditor = WidgetEditor.get('pbDatePicker');
assets = widgetEditor.assets().list();
let firstAsset = assets.first();
expect(firstAsset.element(by.css('#editAsset')).isEnabled()).toBeFalsy();
expect(firstAsset.element(by.css('#deleteAsset')).isEnabled()).toBeFalsy();
});
it('should allow to edit a local asset', function () {
......
......@@ -11,10 +11,11 @@ describe('custom widget test', function() {
editor.addCustomWidget('customAwesomeWidget');
// then we should be able to edit it
$('#edit-widget').click();
$('#widgetActionButton').click();
$('#editAction').click();
// then we should go the custom widget edition
expect($('.EditorHeader-brand').getText()).toBe('CUSTOM WIDGET EDITOR');
expect($('.EditorHeader-brand').getText()).toBe('WIDGET EDITOR');
});
});
......@@ -33,7 +33,7 @@ describe('home page', function() {
it('should navigate to a widget', function() {
$$('.ArtifactList-widget a').first().click();
expect($('.EditorHeader-brand').getText()).toBe('CUSTOM WIDGET EDITOR');
expect($('.EditorHeader-brand').getText()).toBe('WIDGET EDITOR');
});
it('should create a layout', function() {
......@@ -74,7 +74,7 @@ describe('home page', function() {
it('should create a widget with widget tab selected', function() {
home.openTab('widget');
home.createDefault('testWidget');
expect($('.EditorHeader-brand').getText()).toBe('CUSTOM WIDGET EDITOR');
expect($('.EditorHeader-brand').getText()).toBe('WIDGET EDITOR');
});
it('should not create a page with space or special characters in name', function() {
......@@ -115,7 +115,7 @@ describe('home page', function() {
it('should create a widget', function() {
home.createWidget('test');
expect($('.EditorHeader-brand').getText()).toBe('CUSTOM WIDGET EDITOR');
expect($('.EditorHeader-brand').getText()).toBe('WIDGET EDITOR');
});
it('should change sort order to alphabetical or chronological', () => {
......
......@@ -10,14 +10,20 @@ describe('properties panel test', function() {
});
it('should display edit button', function() {
expect(element(by.css('#edit-widget')).isDisplayed()).toBe(true);
it('should display action button, then editAction if custom widget, or viewAction if standard widget', function() {
expect(element(by.css('#editAction')).isPresent()).toBe(false);
expect(element(by.css('#widgetActionButton')).isDisplayed()).toBe(true);
element(by.css('#widgetActionButton')).click();
expect(element(by.css('#viewAction')).isDisplayed()).toBe(true);
editor = PageEditor.get('empty');
expect(element(by.css('#edit-widget')).isDisplayed()).toBe(false);
expect(element(by.css('#widgetActionButton')).isDisplayed()).toBe(false);
editor.addWidget('container');
expect(element(by.css('#edit-widget')).isDisplayed()).toBe(false);
expect(element(by.css('#widgetActionButton')).isDisplayed()).toBe(false);
editor.addCustomWidget('customAwesomeWidget');
expect(element(by.css('#edit-widget')).isDisplayed()).toBe(true);
expect(element(by.css('#widgetActionButton')).isDisplayed()).toBe(true);
element(by.css('#widgetActionButton')).click();
expect(element(by.css('#viewAction')).isPresent()).toBe(false);
expect(element(by.css('#editAction')).isDisplayed()).toBe(true);
});
it('should display default properties on container', function() {
......
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