How to show/hide fields in the admin UI depending on the value from another field with KeystoneJS Next? - keystonejs

I have a Category model set in schema.ts as follows:
Category: list({
fields: {
name: text(),
type: select({
options: [
{ label: "MultipleChoice", value: "MultipleChoice" },
{ label: "Range", value: "Range" },
],
defaultValue: "...",
isRequired: true,
isUnique: true,
ui: { displayMode: "segmented-control" },
}),
from: integer(),
to: integer(),
options: text()
},
})
And this renders these components in the admin UI:
I'd like to show from and to fields only when Range is selected (hiding options field) and the other way around when MultipleChoice is selected. Is there a way to achieve that with Keystone Next?
I also tried another approach splitting the category types in different models and then relate them somehow with the Category model, but I'm not sure how to do so. Something like:
CategoryRange: list({
ui: {
isHidden: true,
},
fields: {
from: integer(),
to: integer(),
},
}),
CategoryMultipleChoice: list({
ui: {
isHidden: true,
},
fields: {
options: text(),
},
})

Conditional fields were supported in Keystone 4. They haven't been brought forward to Keystone 6 (aka. "Next") yet but they're on the roadmap. I'd expect support for them to arrive in the next few months.
Right now, in Keystone 6, probably the best you could do would be to create a custom field type that collected the "type" and "from/to" fields together. This would let you define an interface in the Admin UI that implemented whatever presentation rules you liked.

Related

Vue/Vuetify Data Table OData Support

I have some odata-enabled endpoints that I'm looking to wire up to data tables in vue and have them automatically support server-side sorting, filtering and paging. The grid in Telerik's Kendo UI supports odata to the point where you tell the component that the data source type is odata and it automatically "just works" (https://demos.telerik.com/kendo-ui/grid/remote-data-binding) because odata is a well-defined standard.
I'm wondering if there is support for odata in other common vue-specific UI frameworks like vuetify. I've looked at vuetify specifically and it does seem to support server-side operations, but it's not clear to me how much custom logic I'm going to need to write in order to use odata since I haven't been able to find any specific examples.
Here is the source from the example linked above that shows how clean it is to wire up declaritively without any additional boilerplate logic:
$(document).ready(function() {
$("#grid").kendoGrid({
dataSource: {
type: "odata",
transport: {
read: "https://demos.telerik.com/kendo-ui/service/Northwind.svc/Orders"
},
schema: {
model: {
fields: {
OrderID: { type: "number" },
Freight: { type: "number" },
ShipName: { type: "string" },
OrderDate: { type: "date" },
ShipCity: { type: "string" }
}
}
},
pageSize: 20,
serverPaging: true,
serverFiltering: true,
serverSorting: true
},
height: 550,
filterable: true,
sortable: true,
pageable: true,
columns: [{
field:"OrderID",
filterable: false
},
"Freight",
{
field: "OrderDate",
title: "Order Date",
format: "{0:MM/dd/yyyy}"
}, {
field: "ShipName",
title: "Ship Name"
}, {
field: "ShipCity",
title: "Ship City"
}
]
});
});
Vuetify doesn't seem to support odata yet, so your best bet is probably to write a client-side js solution.
Probably using one of these libraries.
O.js seems to be rather simple.

Display all associated children with wsapi.Store

I've been searching for a way to display all the defects for a project and it's
children in Rally, however I can't seem to find the right way to go about this.
In the constructor it seems there is no parent property, however I've seen this property used in previous versions using wsapiStore. What is the correct way of doing this now?
Here's a look at my code:
config.json:
{
"name": "BasicRallyGrid",
"className": "CustomApp",
"server": "https://rally1.rallydev.com",
"sdk": "2.1",
"javascript": [
"App.js"
],
"css": [
"app.css"
]
}
Call to Store:
this.myStore= Ext.create('Rally.data.wsapi.Store', {
model: 'Defect',
autoLoad: true,
filters: myFilters,
/*filters: Ext.create('Rally.data.wsapi.Filter', {
property: 'Parent',
operator: '=',
value: "SomeParent"
}),
listeners: {
load: function (myStore) {
if (!this.myStore) {
this._createGrid(myStore);
}
},
scope: this
},
fetch: ['FormattedID', 'Name', 'Severity', 'Iteration', 'Project']
});
}
},
My Grid code:
_createGrid: function (myStore) {
this.myGrid = Ext.create('Rally.ui.grid.Grid', {
store: myStore,
columnCfgs: [
'FormattedID', 'Name', 'Severity', 'Iteration', 'Project'
]
});
this.add(this.myGrid);
Any help would be greatly appreciated. Also, as this is my first question here, if I broke some etiquette please let me know so I can avoid it in future posts.
Thank you!
You just need to use project scoping... Check out this guide: https://help.rallydev.com/apps/2.1/doc/#!/guide/data_stores-section-scoping
By default your store will inherit your global scoping, so I would have expected it to just work, but depending on what you're doing you may have to explicitly specify your project scope + up/down.

Set Keystone List "noedit" based on field value?

I would like to make my Keystone List object editable only if the object is not published. Here's my reduced List definition:
var Campaign = new keystone.List('Campaign', {
nodelete: true,
track: {
createdAt: true,
},
});
Campaign.add({
...,
publish: {
type: Types.Boolean,
required: false,
initial: false,
dependsOn: {
publishedOn: '',
},
},
publishedOn: {
type: Types.Datetime,
label: 'Published On',
hidden: true,
},
});
Is it possible to set noedit only if publishedOn is not null? I'm trying to prevent the object from being modified after it's "published", and am coming up short on examples.
The noedit property for a list or field is part of the model definition, and not part of the definition of an individual item. There is no way to mark a single item as uneditable in the admin UI unless you want to apply it to the field or model as a whole.
If you are less worried about clarity, and more worried about ensuring it cannot be edited, you could try the following:
Campaign.schema.post('init', function () {
if (this.published) this.invalidate('Published items cannot be edited');
});
This will cause an error to be thrown if you attempt to save the item once it is published.
While you could use dependsOn: { published: { $exists: true } } to filter fields, this would hide the information.
Interestingly, I was able to make the publish field simply check itself:
Campaign.add({
...,
publish: {
type: Types.Boolean,
required: false,
initial: false,
dependsOn: {
publish: false,
},
},
...,
});
and now it only shows if publish is false, which works exactly as I hoped it would.

How to add custom data in a story board?

Question 1: Is it possible to add custom data (Ex: testcase count : 5) to each card in a story board? If so, how? I couldn't find an example or specific information in documentation.
Question 2: Is it possible to get testcase count (including child story testcases)for a highlevel story in one query?
Please let me know. Here's my code
Ext.define('Rally.Story.View', {
extend: 'Rally.app.App',
launch: function() {
this.add({
xtype: 'rallyfieldvaluecombobox',
fieldLabel: 'Filter by Target Release:',
model: 'UserStory',
field: 'c_TargetRelease',
value: '15.0',
listeners: {
select: this._onSelect,
ready: this._onLoad,
scope: this
}
});
},
_onLoad: function() {
this.add({
xtype: 'rallycardboard',
types: ['User Story'],
attribute: 'ScheduleState',
readOnly: true,
fetch: ['Name', 'TestCases', 'c_StoryType', 'c_TargetRelease', 'PlanEstimate', 'Priority', 'TaskEstimateTotal', 'TaskRemainingTotal'],
context: this.getContext(),
cardConfig: {
editable: false,
showIconsAndHighlightBorder: false,
fields: ['Name', 'c_StoryType', 'c_TargetRelease', 'PlanEstimate', 'c_PriorityBin', 'Parent', 'TestCases', 'TaskEstimateTotal', 'TaskRemainingTotal']
},
storeConfig: {
filters: [
{
property: 'c_StoryType',
value: 'SAGA Feature'
},
{
property: 'c_TargetRelease',
operator: '=',
value: this.down('rallyfieldvaluecombobox').getValue()
}
]
}
});
},
_onSelect: function() {
var board = this.down('rallycardboard');
board.refresh({
storeConfig: {
filters: [
{
property: 'c_StoryType',
value: 'SAGA Feature'
},
{
property: 'c_TargetRelease',
operator: '=',
value: this.down('rallyfieldvaluecombobox').getValue()
}
]
}
});
},
});
Here's a sample card I made that contains the test case count:
You can add a field by simply including an object with some rendering information in it instead of just a simple string in the fields array in the cardConfig:
cardConfig: {
fields: [
'Name', //simple string field to show
{
name: 'TCCount', //field name
hasValue: function() {return true;}, //always show this field
renderTpl: Ext.create('Rally.ui.renderer.template.LabeledFieldTemplate', {
fieldLabel: 'Test Case Count', //the field label
valueTemplate: Ext.create('Ext.XTemplate',
['{[this.getTestCaseCount(values)]}',
{getTestCaseCount: function(data) { return data.Summary.TestCases.Count;}}])
})
},
//additional string fields
'PlanEstimate', 'Parent', 'TestCases', 'TaskEstimateTotal', 'TaskRemainingTotal']
}
This ended up being less straightforward than I thought it might be, but at least it is doable. The key part is using the LabeledFieldTemplate, specifying a field label and a value template to actually render the content.
You'll also notice the little beaker status icon in the footer which is automatically rendered because TestCases was included in the fields list.
As for your second question there is no roll up field on story for the total number of test cases included on child stories.

noEntryText attribute for ComboBoxes won't apply for Custom Fields?

I'm currently working with comboboxes and filters to implement an option to select all instances of a desired field. With that said, I know to use the noEntryText attribute to set what originally defaults to "--No Entry--" as "All". However, the changes do not seem to apply when I use this on custom fields provided by my Web Services API (those fields with the "c_" before them).
Strangely enough though, this convention works for other fields that I use that do not have the "c_" before them. So is this a known defect just for custom fields or is there a workaround to this issue?
I filed a defect that replacing noEntryText to a custom value works for rallyfieldvaluecombobox for standard fields, but not custom fields.
If rallyfieldvaluecombobox uses a standard field, e.g. Environment, then no entry can be replaced successfully:
Here is the js file:
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
launch: function() {
this.add({
xtype: 'rallyfieldvaluecombobox',
itemId: 'aBox',
fieldLabel: 'Filter by filed:',
model: 'defect',
//field: 'c_CustomBox',
field: 'Environment',
noEntryText: 'All',
useNullForNoEntryValue: true,
allowNoEntry: true,
listeners: {
select: this._onSelect,
ready: this._onLoad,
scope: this
}
});
},
_onLoad: function() {
this.add({
xtype: 'rallygrid',
columnCfgs: [
'FormattedID',
'Name',
//'c_CustomBox'
'Environment'
],
context: this.getContext(),
storeConfig: {
model: 'defect',
filters: [this._getStateFilter()]
},
width: 500
});
},
_getStateFilter: function() {
if (!this.down('#aBox').getValue()) {
return 1;
}
else{
return {
//property: 'c_CustomBox',
property: 'Environment',
operator: '=',
value: this.down('#aBox').getValue()
};
}
},
_onSelect: function() {
var grid = this.down('rallygrid'),
store = grid.getStore();
store.clearFilter(true);
store.filter(this._getStateFilter());
}
});
If the same rallyfieldvaluecombobox uses a custom field, then no entry string cannot be replaced:
In both cases the functionality of replacing the filtering behavior works fine.