namespace-browser: Service Signature Compatibility Update
- Adapated Signature now reveals more information.
+ argument type information (inArgs only, not outArgs)
+ doc strings (shown in core-tooltip) for methods and interfaces
- Polymer tweaking
+ label attribute was removed on paper-button
+ core-tooltip's internal overlay has .core-tooltip as its class
- Mercury tweaking
+ span was added around various text nodes.
For some reason, mercury is not diffing them properly.
Screenshots:
* https://screenshot.googleplex.com/UEPot1EF9t
* https://screenshot.googleplex.com/ft28NTLWZX
Change-Id: I92bc8c40830fd9842d4e1ba2012200eba1ff6ab3
diff --git a/src/components/browse/index.css b/src/components/browse/index.css
index fe8f78a..bc1afd6 100644
--- a/src/components/browse/index.css
+++ b/src/components/browse/index.css
@@ -107,17 +107,11 @@
color: var(--color-text-primary-invert);
}
-.tooltip:hover::shadow .polymer-tooltip,
-.tooltip:focus::shadow .polymer-tooltip {
- opacity: 1;
- transform: translate3d(0, 0, 0);
-}
-
-.tooltip::shadow .polymer-tooltip {
- opacity: 0;
- transition: all 300ms cubic-bezier(0,1.92,.99,1.07);
- transition-delay: 0.5s;
- transform: translate3d(0, -10px, 0);
+.tooltip::shadow .core-tooltip {
+ width: 36em;
+ line-height: 0.9em; /* overrides Polymer's 6px line-height */
+ white-space: pre-wrap;
+ font-size: var(--size-font-xsmall);
}
.breadcrumbs {
@@ -185,6 +179,7 @@
.search-box core-tooltip {
width: 100%;
+ font-size: var(--size-font-xsmall);
}
.search-box .icon, .namespace-box .icon {
@@ -194,7 +189,7 @@
}
.search-box .input {
- font-size: var(--size-font-xsmall)
+ font-size: var(--size-font-xsmall);
}
/* Make space for the clear search icon */
diff --git a/src/components/browse/item-details/index.css b/src/components/browse/item-details/index.css
index ec35868..2ff892f 100644
--- a/src/components/browse/item-details/index.css
+++ b/src/components/browse/item-details/index.css
@@ -18,6 +18,10 @@
overflow: hidden;
}
+.tooltip.method-tooltip::shadow .core-tooltip {
+ width: 24em;
+}
+
.method-input .label {
align-items: center;
overflow: hidden;
@@ -78,6 +82,10 @@
color: var(--color-error);
}
+core-icon.info {
+ color: var(--color-dark);
+}
+
/* Chrome, Safari, Opera */
@-webkit-keyframes myfirst {
0% {background:cyan; width:0%;}
diff --git a/src/components/browse/item-details/index.js b/src/components/browse/item-details/index.js
index 18d5d46..b0f83e9 100644
--- a/src/components/browse/item-details/index.js
+++ b/src/components/browse/item-details/index.js
@@ -127,11 +127,41 @@
} else {
typeName = 'Intermediary Name';
}
+
var displayItems = [
renderFieldItem('Name', (item.objectName || '<root>')),
renderFieldItem('Type', typeName, typeDescription)
];
+ if (item.isServer) {
+ // Display each service description and show it.
+ var serviceDescs = [];
+ var descs = state.item.serverInfo.signature.pkgNameDescriptions;
+ Object.keys(descs).forEach(function(pkgName) {
+ var desc = descs[pkgName];
+
+ // Use an info icon whose tooltip reveals the description.
+ serviceDescs.push(h('div', [
+ h('core-tooltip.tooltip', {
+ 'label': desc || '<no description>',
+ 'position': 'right'
+ }, h('core-icon.icon.info', {
+ 'icon': new AttributeHook('info')
+ })),
+ h('span.margin-left-xxsmall', pkgName)
+ ]));
+ });
+
+ if (serviceDescs.length > 0) {
+ displayItems.push(
+ renderFieldItem('Interfaces', h('div', {
+ 'vertical': new AttributeHook(true),
+ 'layout': new AttributeHook(true)
+ }, serviceDescs))
+ );
+ }
+ }
+
return [
h('div', displayItems)
];
@@ -155,7 +185,7 @@
function renderMethodSignatures(state, events) {
var sig = state.item.serverInfo.signature;
if (!sig) {
- return h('div', 'No method signature');
+ return h('div', h('span', 'No method signature'));
}
var methods = [];
@@ -184,7 +214,7 @@
function renderMethodOutput(state) {
var outputs = state.methodOutputs[state.item.objectName];
if (outputs === undefined) {
- return h('div.method-output', 'No method output');
+ return h('div.method-output', h('span', 'No method output'));
}
var outputRows = [h('tr', [
h('th', '#'),
diff --git a/src/components/browse/item-details/method-end.js b/src/components/browse/item-details/method-end.js
index fd75462..2a25f1f 100644
--- a/src/components/browse/item-details/method-end.js
+++ b/src/components/browse/item-details/method-end.js
@@ -1,3 +1,4 @@
+var h = require('mercury').h;
var smartService = require('../../../services/smart/service');
var log = require('../../../lib/log')(
'components:browse:item-details:method-end');
@@ -31,9 +32,9 @@
// Do not process results we expect to be empty.
// TODO(alexfandrianto): Streaming results are ignored with this logic.
- var expectedOutArgs = sig.get(method).numOutArgs;
+ var expectedOutArgs = sig.get(method).outArgs.length;
if (expectedOutArgs === 1) { // Error is the only possible out argument.
- replaceResult(state, data.runID, '<ok>');
+ replaceResult(state, data.runID, h('span', '<ok>'));
return;
}
diff --git a/src/components/browse/item-details/method-form/index.js b/src/components/browse/item-details/method-form/index.js
index 7b75ef7..c459f14 100644
--- a/src/components/browse/item-details/method-form/index.js
+++ b/src/components/browse/item-details/method-form/index.js
@@ -148,7 +148,7 @@
param.inArgs.map(function(inArg, i) {
return smartService.predict(
'learner-method-input',
- _.assign({argName: inArg}, input)
+ _.assign({argName: inArg.name}, input)
).then(function(inputSuggestion) {
state.inputSuggestions.put(i, inputSuggestion);
});
@@ -287,7 +287,7 @@
// Return immediately if we don't need arguments or haven't expanded.
if (state.args.length === 0 || !state.expanded) {
- return h('div.method-input', methodNameHeader);
+ return makeMethodTooltip(state, h('div.method-input', methodNameHeader));
}
// Render the stars first, and if there's extra room, the recommendations.
@@ -305,7 +305,18 @@
var runButton = renderRPCRunButton(state, events);
var footer = h('div.method-input-expanded', [argForm, starButton, runButton]);
- return h('div.method-input', [methodNameHeader, recs, footer]);
+ return makeMethodTooltip(state,
+ h('div.method-input', [methodNameHeader, recs, footer]));
+}
+
+/*
+ * Wrap the method form with a tooltip.
+ */
+function makeMethodTooltip(state, child) {
+ return h('core-tooltip.tooltip.method-tooltip', {
+ 'label': state.signature.get(state.methodName).doc || '<no description>',
+ 'position': 'top'
+ }, child);
}
/*
@@ -316,9 +327,7 @@
return renderInvocation(state, events);
}
var labelText = getMethodSignature(state);
- var label = h('div.label', {
- 'title': labelText
- }, labelText);
+ var label = makeMethodLabel(labelText);
var expand = h('a.drill', {
'href': 'javascript:;',
@@ -342,7 +351,12 @@
var param = state.signature.get(methodName);
var text = methodName + '(';
for (var i = 0; i < param.inArgs.length; i++) {
- var arg = args !== undefined ? args[i] : param.inArgs[i];
+ var arg = '';
+ if (args !== undefined) {
+ arg = args[i];
+ } else {
+ arg = param.inArgs[i].name + ' ' + param.inArgs[i].type.toString();
+ }
if (i > 0) {
text += ',';
}
@@ -394,9 +408,7 @@
var noArgs = argsStr === undefined;
var args = noArgs ? [] : JSON.parse(argsStr);
var labelText = getMethodSignature(state, args);
- var label = h('div.label', {
- 'title': labelText
- }, labelText);
+ var label = makeMethodLabel(labelText);
var runButton = h('a.drill', {
'href': 'javascript:;',
@@ -422,12 +434,21 @@
}
/*
+ * Render a method label using labelText.
+ */
+function makeMethodLabel(labelText) {
+ return h('div.label', {
+ 'title': labelText
+ }, labelText);
+}
+
+/*
* Draws a single method argument input using the paper-autocomplete element.
* Includes a placeholder and suggestions from the internal state.
*/
function renderMethodInput(state, index) {
var methodName = state.methodName;
- var argName = state.signature.get(methodName).inArgs[index];
+ var argName = state.signature.get(methodName).inArgs[index].name;
var inputSuggestions = state.inputSuggestions[index];
var args = state.args;
@@ -466,10 +487,12 @@
'raised': new AttributeHook('true'),
'ev-click': mercury.event(events.starAction, {
star: true
- }),
- 'label': 'STAR'
+ })
},
- renderStarIcon(false)
+ [
+ renderStarIcon(false),
+ h('span', 'Star')
+ ]
);
return starButton;
}
@@ -483,10 +506,12 @@
{
'href': 'javascript:;',
'raised': new AttributeHook('true'),
- 'ev-click': getRunEvent(state, events, state.args),
- 'label': 'RUN'
+ 'ev-click': getRunEvent(state, events, state.args)
},
- renderPlayIcon()
+ [
+ renderPlayIcon(),
+ h('span', 'Run')
+ ]
);
return runButton;
}
diff --git a/src/components/browse/item-details/plugins/default.js b/src/components/browse/item-details/plugins/default.js
index 0662125..b6df1f9 100644
--- a/src/components/browse/item-details/plugins/default.js
+++ b/src/components/browse/item-details/plugins/default.js
@@ -1,3 +1,5 @@
+var h = require('mercury').h;
+
module.exports = {
'shouldFormat': shouldFormat,
'format': format
@@ -14,5 +16,5 @@
* By default, the input is returned as prettified JSON.
*/
function format(input) {
- return JSON.stringify(input, null, 2);
+ return h('span', JSON.stringify(input, null, 2));
}
\ No newline at end of file
diff --git a/src/components/browse/item-details/plugins/empty.js b/src/components/browse/item-details/plugins/empty.js
index 38d8e75..dceccea 100644
--- a/src/components/browse/item-details/plugins/empty.js
+++ b/src/components/browse/item-details/plugins/empty.js
@@ -1,3 +1,5 @@
+var h = require('mercury').h;
+
module.exports = {
'shouldFormat': shouldFormat,
'format': format
@@ -15,5 +17,5 @@
* Indicate that nothing was there.
*/
function format(input) {
- return '<no data>';
+ return h('span', '<no data>');
}
\ No newline at end of file
diff --git a/src/components/browse/item-details/plugins/error.js b/src/components/browse/item-details/plugins/error.js
index 18d8ca9..73f3739 100644
--- a/src/components/browse/item-details/plugins/error.js
+++ b/src/components/browse/item-details/plugins/error.js
@@ -17,7 +17,7 @@
* Print the error with a dangerous-looking icon.
*/
function format(input) {
- return h('span', [
+ return h('div', [
h('core-icon.error', {
'icon': new AttributeHook('error')
}),
diff --git a/src/components/browse/item-details/plugins/histogram.js b/src/components/browse/item-details/plugins/histogram.js
index 15bb923..e908eb6 100644
--- a/src/components/browse/item-details/plugins/histogram.js
+++ b/src/components/browse/item-details/plugins/histogram.js
@@ -1,3 +1,4 @@
+var h = require('mercury').h;
var histogram = require('bars');
module.exports = {
@@ -23,5 +24,5 @@
input.buckets.forEach(function(obj) {
histData[obj.lowBound] = obj.count;
});
- return histogram(histData, { bar: '*', width: 20 });
+ return h('span', histogram(histData, { bar: '*', width: 20 }));
}
\ No newline at end of file
diff --git a/src/components/common-style/defaults.css b/src/components/common-style/defaults.css
index 5817a5a..33efad7 100644
--- a/src/components/common-style/defaults.css
+++ b/src/components/common-style/defaults.css
@@ -64,9 +64,24 @@
paper-button {
background-color: var(--color-grey-light);
margin: var(--size-space-xsmall);
- max-height: 3em;
+}
+
+/*
+ * The paper-button looks better with reduced padding.
+ * Polymer defaults to 0.70em for padding-top and padding-bottom.
+ */
+paper-button::shadow .button-content {
+ padding-top: 0.35em;
+ padding-bottom: 0.35em;
}
paper-button > core-icon {
margin-right: var(--size-space-xsmall);
+}
+
+/*
+ * Applies a tiny margin to the left of the element.
+ */
+.margin-left-xxsmall {
+ margin-left: var(--size-space-xxsmall);
}
\ No newline at end of file
diff --git a/src/components/common-style/sizes.css b/src/components/common-style/sizes.css
index aa9f600..e8d37d2 100644
--- a/src/components/common-style/sizes.css
+++ b/src/components/common-style/sizes.css
@@ -2,7 +2,7 @@
:root {
/* font-size */
- --size-font-xsmall: 0.65em;
+ --size-font-xsmall: 0.7em;
--size-font-small: 0.9em;
--size-font-normal: 1em;
--size-font-large: 1.1em;
diff --git a/src/services/namespace/service.js b/src/services/namespace/service.js
index 464fdec..df8e74f 100644
--- a/src/services/namespace/service.js
+++ b/src/services/namespace/service.js
@@ -254,11 +254,11 @@
* a service without containing unnecessary information.
* TODO(alexfandrianto): This heuristic comes close, but it does not properly
* distinguish services from each other.
- * Once available, add type info, streaming info, interface name, etc.
+ * The adapted signature now has type info, streaming info, interface name, etc.
*/
-function hashSignature(signature) {
- var cp = adaptSignature([]);
- signature.forEach(function(method, methodName) {
+function hashSignature(adaptedSignature) {
+ var cp = [];
+ adaptedSignature.forEach(function(method, methodName) {
cp[methodName] = method.inArgs.length;
});
return jsonStableStringify(cp);
diff --git a/src/services/namespace/signature-adapter.js b/src/services/namespace/signature-adapter.js
index d1f364f..e99f351 100644
--- a/src/services/namespace/signature-adapter.js
+++ b/src/services/namespace/signature-adapter.js
@@ -2,15 +2,18 @@
module.exports = adapt;
/*
- * Adapts from IPC service Signatures to a custom signature struct specific
- * to this application.
- * TODO(aghasssemi) Consider separate signature instead of merging?
+ * Adapts from IPC service Signatures to a custom signature Map-specific
+ * to this application. Service methods, pkgName, and descriptions are added.
+ * TODO(aghasssemi): Consider separate signature instead of merging?
*/
function adapt(signatures) {
var adaptedSig = new Map();
-
+ adaptedSig.pkgNameDescriptions = [];
signatures.forEach(function(sig) {
- sig.methods.forEach( function(method) {
+ if (sig.name) {
+ adaptedSig.pkgNameDescriptions[sig.pkgPath + '.' + sig.name] = sig.doc;
+ }
+ sig.methods.forEach(function(method) {
var key = vom.MiscUtil.uncapitalize(method.name);
adaptedSig.set(key, method);
});