Rally App SDK 2.0: how to get tooltip on - rally

If I have a rallygrid of PortfolioItems and I include PercentDoneByStoryPlanEstimate in the column configs, I get the progress bar as expected. But when I hover over the progress bar, I don't see the tooltip with extra information that I see in Rally itself.
How do I get the tooltip to display in an app?
Sample code:
Rally.onReady(function() {
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
launch: function() {
Rally.data.ModelFactory.getModel({
type: 'PortfolioItem',
success: function(model) {
this.grid = this.add({
xtype: 'rallygrid',
model: model,
columnCfgs: [
'FormattedID',
'Name',
'PercentDoneByStoryPlanEstimate'
]
});
},
scope: this
});
}
});
Rally.launchApp('CustomApp', {
name: 'Grid Example'
});
});

You just need to specify a viewConfig and add the 'rallypercentdonetooltip' plugin like so:
success: function(model) {
this.grid = this.add({
xtype: 'rallygrid',
model: model,
columnCfgs: [
'Name',
'FormattedID',
'PercentDoneByStoryPlanEstimate'
],
viewConfig:{
plugins:[
{ptype:'rallypercentdonetooltip'}
]
}
});

Related

Filter Control for Grid/TreeGrid

Is the Filter Control component available for the Grid/TreeGrid (similar to the filter within the Portfolio Items dashboard)? Per the SDK 2.0 documentation, it seems this component is only available for the GridBoard.
gridboard is a wrapper around treegrid or board, and filtercontrol plugin should work with a TreeGrid. Here is an example of a tree grid with enableHierarchy set to true and a rallygridboardcustomfiltercontrol plugin:
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
launch: function() {
Ext.create('Rally.data.wsapi.TreeStoreBuilder').build({
models: ['userstory'],
autoLoad: true,
enableHierarchy: true
}).then({
success: this._onStoreBuilt,
scope: this
});
},
_onStoreBuilt: function(store) {
var modelNames = ['userstory'];
var context = this.getContext();
this.add({
xtype: 'rallygridboard',
modelNames: modelNames,
context:context,
enableHierarchy: 'true',
toggleState: 'grid',
plugins: [
{
ptype: 'rallygridboardcustomfiltercontrol',
filterControlConfig: {
modelNames: modelNames
}
}
],
cardBoardConfig: {
attribute: 'ScheduleState'
},
gridConfig: {
store: store,
columnCfgs: [
'Name',
'ScheduleState',
'Owner',
'PlanEstimate'
]
},
height: this.getHeight()
});
}
});

How to create a combobox of possible values

Is there a way to dynamically populate a combobox with the attributes a certain property of an artifact can take on?
e.g.
I have a custom field set up on User Stories. I want to be able to populate a combobox with all the possible values for this custom field without hard-coding it in.
In the code below the combobox is automatically populated with the allowed values of the custom field of dropdown type:
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
items: [
{
xtype: 'container',
itemId: 'kbFilter'
},
{
xtype: 'container',
itemId: 'grid',
width: 800
}
],
launch: function() {
this.down('#kbFilter').add({
xtype: 'checkbox',
cls: 'filter',
boxLabel: 'Filter table by custom field',
id: 'kbCheckbox',
scope: this,
handler: this._onSettingsChange
});
this.down('#kbFilter').add({
xtype: 'rallyattributecombobox',
cls: 'filter',
model: 'Defect',
field: 'MyKB',
listeners: {
ready: this._onKBComboBoxLoad,
select: this._onKBComboBoxSelect,
scope: this
}
});
},
_onKBComboBoxLoad: function(comboBox) {
this.kbComboBox = comboBox;
Rally.data.ModelFactory.getModel({
type: 'Defect',
success: this._onModelRetrieved,
scope: this
});
},
_getFilter: function() {
var filter = [];
if (Ext.getCmp('kbCheckbox').getValue()) {
filter.push({
property: 'MyKB',
operator: '=',
value: this.kbComboBox.getValue()
});
}
return filter;
},
_onKBComboBoxSelect: function() {
if (Ext.getCmp('kbCheckbox').getValue()) {
this._onSettingsChange();
}
},
_onSettingsChange: function() {
this.grid.filter(this._getFilter(), true, true);
},
_onModelRetrieved: function(model) {
this.grid = this.down('#grid').add({
xtype: 'rallygrid',
model: model,
columnCfgs: [
'FormattedID',
'Name',
'MyKB'
],
storeConfig: {
context: this.context.getDataContext(),
filters: this._getFilter()
},
showPagingToolbar: false
});
}
});
In this example I have a dropdown field with Name: myKB and Display Name: My KB.
In the WS API the name shows with prepended c_, as in c_myKB.
However, if I use c_myKB this error comes up:
Uncaught Rally.ui.combobox.FieldValueComboBox._populateStore(): field config must be specified when creating a Rally.ui.combobox.FieldValueComboBox
Use the display name of the field, without spaces.
Here is a screenshot showing this app in action:

Rally SDK 2.0 Display User Story Parent (Feature) and Sort by Parent in rallygrid

Having trouble showing parent feature of user stories and sorting by that same parent field. Here's my code. I see empty value in Parent column unless the parent is another user story. And I am not able to sort by Parent field.
Your help would be greatly appreciated!
Thanks!
<script type="text/javascript">
Rally.onReady(function() {
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
items: [
{
xtype: 'container',
itemId: 'iterationFilter'
},
{
xtype: 'container',
itemId: 'grid',
width: 800
}
],
launch: function() {
this.down('#iterationFilter').add({
xtype: 'rallyiterationcombobox',
cls: 'filter',
model: 'UserStory',
field: 'Iteration',
listeners: {
ready: this._onIterationComboBoxLoad,
select: this._onIterationComboBoxSelect,
scope: this
}
});
},
_onIterationComboBoxLoad: function(comboBox) {
this.iterationComboBox = comboBox;
Rally.data.ModelFactory.getModel({
type: 'UserStory',
success: this._onModelRetrieved,
scope: this
});
},
_getFilter: function() {
var filter = [];
filter.push({
property: 'Iteration',
operator: '=',
value: this.iterationComboBox.getValue()
});
return filter;
},
_onIterationComboBoxSelect: function() {
this._onSettingsChange();
},
_onSettingsChange: function() {
this.grid.filter(this._getFilter(), true, true);
},
_onModelRetrieved: function(model) {
this.grid = this.down('#grid').add({
xtype: 'rallygrid',
model: model,
columnCfgs: [
'FormattedID',
'Name',
'Plan Estimate',
'Parent',
'Schedule State',
'StoryType'
],
storeConfig: {
context: this.context.getDataContext(),
filters: this._getFilter()
},
showPagingToolbar: true,
enableEditing: false
});
}
});
});
Rally.launchApp('CustomApp', {
name: 'Defect Dashboard'
});
</script>
User Stories have two different fields for their Parent. If the Parent is another story it uses Parent. In the case where the Parent is a Portfolio Item like a Feature the parent will be called PortfolioItem.
You can see the fields on user story by looking at our webservice docs.
In your example you would have to change your column configs to include PorfolioItem
columnCfgs: [
'FormattedID',
'Name',
'Plan Estimate',
'PortfolioItem',
'Schedule State',
'StoryType'
],
I was at least able to show the name of feature for each user story. I am still having an issue to make it sortable though. :(
<!DOCTYPE html>
<html>
<head>
<title>Grid Example</title>
<script type="text/javascript" src="/apps/2.0p4/sdk.js?wsapiVersion=1.38"></script>
<script type="text/javascript">
Rally.onReady(function() {
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
items: [
{
xtype: 'container',
itemId: 'iterationFilter'
},
{
xtype: 'container',
itemId: 'grid'
//width: 800
}
],
launch: function() {
this.down('#iterationFilter').add({
xtype: 'rallyiterationcombobox',
cls: 'filter',
model: 'UserStory',
field: 'Iteration',
listeners: {
ready: this._onIterationComboBoxLoad,
select: this._onIterationComboBoxSelect,
scope: this
}
});
},
_onIterationComboBoxLoad: function(comboBox) {
this.iterationComboBox = comboBox;
Rally.data.ModelFactory.getModel({
type: 'UserStory',
success: this._onModelRetrieved,
scope: this
});
},
_getFilter: function() {
var filter = [];
filter.push({
property: 'Iteration',
operator: '=',
value: this.iterationComboBox.getValue()
});
return filter;
},
_onIterationComboBoxSelect: function() {
this._onSettingsChange();
},
_onSettingsChange: function() {
this.grid.filter(this._getFilter(), true, true);
},
_onModelRetrieved: function(model) {
this.grid = this.down('#grid').add({
xtype: 'rallygrid',
model: model,
columnCfgs: [
'FormattedID',
'Name',
'PlanEstimate',
{
text: 'Feature',
dataIndex: 'PortfolioItem',
renderer: function(value, metaData, record, rowIndex, colIndex, store, view) {
if (value != null) {
return value.Name;
}
return '';
}
},
'ScheduleState',
'StoryType'
],
storeConfig: {
context: this.context.getDataContext(),
remoteSort: false,
filters: this._getFilter()
},
showPagingToolbar: true,
enableEditing: false
});
}
});
});
Rally.launchApp('CustomApp', {
name: 'Defect Dashboard'
});
</script>
<style type="text/css">
.filter {
float: left;
margin: 5px 5px;
vertical-align: middle;
}
</style>
</head>
<body></body>
</html>
Looking at your revised solution with the renderer, all you need to add is a doSort method to the custom column you have set up. Make sure you set remoteSort to false on the store, then you can override the sorting with a method:
doSort: function(state) {
var ds = this.up('grid').getStore();
var field = this.getSortParam();
ds.sort({
property: field,
direction: state,
sorterFn: function(v1, v2){
v1 = v1.get(field);
v2 = v2.get(field);
return v1.length > v2.length ? 1 : (v1.length < v2.length ? -1 : 0);
}
});
}
This sorter happens to sort by the length of the the field, but you can change it to do what you want. See Ext js sorting custom column by contents

Sencha Touch 2 add icons to nestedlist

I am trying to add images to a nested list using the getItemTextTpl method of NestedList. Can you please take a look at the following code and let me know how to fix it? This was developed using Sencha Architect. Thanks for your help.
Ext.define('myapp.view.ListContainer', {
extend: 'Ext.Container',
alias: 'widget.listcontainer',
config: {
layout: {
type: 'fit'
},
tpl: [
''
],
items: [
{
xtype: 'nestedlist',
id: 'myList',
itemId: 'mynestedlist4',
detailCard: {
xtype: 'mytabs'
},
store: 'myStore',
toolbar: {
xtype: 'titlebar',
docked: 'bottom',
ui: 'dark'
}
}
],
listeners: [
{
fn: 'getItemTextTpl',
event: 'getItemTextTpl',
delegate: '#myList'
}
]
},
getItemTextTpl: function(node) {
return '<img class="eventIcon" src="http://localhost/images/test.png">';
}
});
Ext.define('myapp.view.myList', {
extend: 'Ext.dataview.NestedList',
alias: 'widget.mynestedlist',
config: {
id: 'myList',
detailCard: {
xtype: 'mytabs'
},
displayField: 'text',
store: 'myStore'
},
getItemTextTpl: function(recordnode) {
return '<img class="eventIcon" src="http://localhost/images/test.png">';
}
});
Just a quick tip. FontAwesome is a great way to add beautiful icons easily to you application.

Sencha Touch 2 - switch views

I started to use the Sencha Touch 2 MVC, but I can't overcome the problem below.
I have an Ext.application code:
Ext.application({
name: 'ProjectName',
appFolder: APP_URL + "app",
enableQuickTips: true,
extend: 'Ext.app.Controller',
phoneStartupScreen: 'LOGO.png',
controllers: ['Site'],
views: ['Viewport_login', 'Viewport_reg'],
layout: 'vbox',
launch: function() {
//bootstrap
Ext.create("Ext.Container", {
requires: [
],
items: [
Ext.create("ProjectName.view.Viewport_login", {}),
Ext.create("ProjectName.view.Viewport_reg", {})
]
});
return true;
}
});
view 'Viewport_login' code:
Ext.define('ProjectName.view.Viewport_login', {
extend: 'Ext.Panel',
requires: [
'ProjectName.view.Login',
'ProjectName.view.Header'
],
fullscreen: true,
initialize: function() {
console.log("init viewpor_login");
Ext.create("Ext.Container", {
//fullscreen: true,
style: 'background-color: white;',
layout: 'vbox',
fullscreen: true,
scrollable: true,
items: [
{
xtype: 'Bheader'
},
Ext.create("widget.login")
]
});
this.callParent();
}
});
View 'Viewpoer_reg' code:
Ext.define('ProjectName.view.Viewport_reg', {
extend: 'Ext.Panel',
requires: [
'ProjectName.view.Reg',
'ProjectName.view.Header'
],
fullscreen: true,
initialize: function() {
console.log("init viewpor_reg");
Ext.create("Ext.Container", {
//fullscreen: true,
style: 'background-color: white;',
layout: 'vbox',
fullscreen: true,
scrollable: true,
items: [
{
xtype: 'Bheader'
},
Ext.create("widget.reg")
]
});
this.callParent();
}
});
view 'Header' code:
Ext.define('ProjectName.view.Header', {
extend: 'Ext.Panel',
alias: 'Bheader',
xtype: 'Bheader',
requires: [
'Ext.Img'
],
initialize: function() {
console.log("header inited");
},
config: {
cls: 'bg-holder',
items: [
Ext.create("Ext.Img", {
src: BASE_URL + 'assets/images/header3.png',
height: 35,
width: "100%",
style: "background-position: center 0px; "
})
]
}
});
And finally the 'Site' controller's code:
Ext.define('ProjectName.controller.Site', {
extend: 'Ext.app.Controller',
config: {
views: ['Viewport_login', 'Viewport_reg']
},
init: function() {
console.log('Init site controller');
// Start listening for events on views
this.control({
// example of listening to *all* button taps
'#login_button': {
tap: function () {
// HOW CAN I SWITCH TO 'VIEWPORT_REG' VIEW?
}
}
});
},
renderRegFOrm: function() {
},
onLaunch: function() {
console.log('onLaunch site controller');
},
});
First, I have a problem right now: The 'Header' does'nt appear if I load both views ('Viewport_login', 'Viewport_reg') in Container which created in Ext.application launch function. Can anyone help me, why?
Second, in the Controller code you can see the this.control(... section. How can I switch to other view in here?
From looking at your code, it appears that you only want one of login and register to appear at one time. I would recommend looking at the setActiveItem method for containers and switching views in this way.
I didn't understand your first question. Also, didn't understand why you have widget.login and widget.reg classes when you already have views called login and reg (can't you just use those?)