Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ All notable changes to `phalcon/debugbar` are documented here. The format is bas
- A collector no longer sends `widget.icon` and `AbstractCollector` has no `$icon` property, because the browser client never read them. `indicator.icon` does not change. [#62](https://github.com/phalcon/debugbar/issues/62)
- `DebugBarTypes` holds the wire contract and the config shapes, and it lists the panel names, so static analysis rejects an unknown panel name. [#62](https://github.com/phalcon/debugbar/issues/62)
- The debug page in ADR applications uses `Injector::isHtml()` and no longer depends on the injection rules of the bar. [#62](https://github.com/phalcon/debugbar/issues/62)
- `DebugBarTypes` lists the icon names as `icon_type`, so static analysis rejects an unknown `indicator.icon`. A test keeps the list equal to the drawings in `debugbar.js`. An indicator with an unknown icon name now shows no icon, not the gears. [#64](https://github.com/phalcon/debugbar/issues/64)

### Fixed

Expand Down
2 changes: 2 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,8 @@ Time and Memory appear as compact indicators on the right instead of duplicate t
Their indicators remain available whether History is enabled or disabled.
Each participating collector declares its own indicator and semantic value path;
the History collector does not depend on the Time or Memory payload shape.
The `icon` of an indicator must be `clock`, `cogs`, or `search`. With any other name,
the indicator shows its value and no icon.
The rightmost control combines
a search icon with the current HTTP method and URI; it opens or closes request
history instead of using a dedicated History tab. The history browser and collector
Expand Down
72 changes: 41 additions & 31 deletions resources/assets/debugbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,51 +46,60 @@
return node;
}

function indicatorIcon(name) {
var svg = svgElement('svg', {
'aria-hidden': 'true',
'class': 'phalcon-debugbar-indicator-icon',
'viewBox': '0 0 18 18'
});
function gear(cx, cy, radius) {
var group = svgElement('g', {});
group.appendChild(svgElement('circle', {cx: cx, cy: cy, r: radius}));
group.appendChild(svgElement('circle', {cx: cx, cy: cy, r: radius / 3}));
for (var index = 0; index < 8; index++) {
var angle = index * Math.PI / 4;
group.appendChild(svgElement('line', {
x1: cx + Math.cos(angle) * radius,
y1: cy + Math.sin(angle) * radius,
x2: cx + Math.cos(angle) * (radius + 1.5),
y2: cy + Math.sin(angle) * (radius + 1.5)
}));
}
return group;
}

if (name === 'clock') {
// `icon_type` in DebugBarTypes.php lists the same names, and a test compares the two.
var ICONS = {
clock: function (svg) {
svg.appendChild(svgElement('circle', {cx: '9', cy: '9', r: '6.5'}));
svg.appendChild(svgElement('path', {d: 'M9 5.2V9l2.8 1.7'}));
return svg;
}

if (name === 'search') {
},
cogs: function (svg) {
svg.appendChild(gear(6.2, 10.5, 3.1));
svg.appendChild(gear(12.2, 6.2, 2.4));
},
search: function (svg) {
svg.appendChild(svgElement('circle', {cx: '7.5', cy: '7.5', r: '4.5'}));
svg.appendChild(svgElement('path', {d: 'M10.8 10.8l4 4'}));
return svg;
}

function gear(cx, cy, radius) {
var group = svgElement('g', {});
group.appendChild(svgElement('circle', {cx: cx, cy: cy, r: radius}));
group.appendChild(svgElement('circle', {cx: cx, cy: cy, r: radius / 3}));
for (var index = 0; index < 8; index++) {
var angle = index * Math.PI / 4;
group.appendChild(svgElement('line', {
x1: cx + Math.cos(angle) * radius,
y1: cy + Math.sin(angle) * radius,
x2: cx + Math.cos(angle) * (radius + 1.5),
y2: cy + Math.sin(angle) * (radius + 1.5)
}));
}
svg.appendChild(group);
}
};

gear(6.2, 10.5, 3.1);
gear(12.2, 6.2, 2.4);
function indicatorIcon(name) {
if (!Object.prototype.hasOwnProperty.call(ICONS, name)) {
return null;
}

var svg = svgElement('svg', {
'aria-hidden': 'true',
'class': 'phalcon-debugbar-indicator-icon',
'viewBox': '0 0 18 18'
});
ICONS[name](svg);
return svg;
}

function metricIndicator(icon, label, value) {
var indicator = el('button', 'phalcon-debugbar-indicator phalcon-debugbar-metric');
var drawing = indicatorIcon(icon);
indicator.type = 'button';
indicator.title = label;
indicator.appendChild(indicatorIcon(icon));
if (drawing) {
indicator.appendChild(drawing);
}
indicator.appendChild(el('span', 'phalcon-debugbar-indicator-value', scalar(value)));
return indicator;
}
Expand Down Expand Up @@ -590,6 +599,7 @@
if (typeof module !== 'undefined' && module.exports) {
module.exports = {
createHistoryRequestGuard: createHistoryRequestGuard,
iconNames: Object.keys(ICONS),
renderHistoryBrowser: renderHistoryBrowser
};
}
Expand Down
3 changes: 2 additions & 1 deletion src/DebugBar/DebugBarTypes.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
* @phpstan-type exception_panel list<exception_row>
* @phpstan-type log_row array{label: string, message: string, context: string}
* @phpstan-type log_panel list<log_row>
* @phpstan-type indicator array{icon: string, label: string, path: list<string>}
* @phpstan-type icon_type 'clock'|'cogs'|'search'
* @phpstan-type indicator array{icon: icon_type, label: string, path: list<string>}
* @phpstan-type request_widget array{method: list<string>, uri: list<string>}
* @phpstan-type widget array{
* label: string,
Expand Down
86 changes: 86 additions & 0 deletions tests/JavaScript/debugbar.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,50 @@ function panelContract() {
return JSON.parse(fs.readFileSync(require.resolve('../support/assets/DebugBar/panel-contract.json'), 'utf8'));
}

function renderPayload(payload) {
var dataNode = new dom.TestElement('script');
var mount = new dom.TestElement('div');
dataNode.textContent = JSON.stringify(payload);
global.document = dom.createDocument({
'phalcon-debugbar': mount,
'phalcon-debugbar-data': dataNode
});
global.window = {
localStorage: {
getItem: function () {
return null;
},
setItem: function () {}
}
};

delete require.cache[debugbarPath];
require(debugbarPath);

return mount;
}

function renderIndicator(icon) {
return renderPayload({
data: {time: {panel: [], badge: '12.34ms'}},
meta: {
widgets: {
time: {
label: 'Time',
panel: 'list',
indicator: {icon: icon, label: 'Request time', path: ['badge']}
}
}
}
});
}

function iconShapes(icon) {
return icon.children.map(function (shape) {
return shape.tagName;
});
}

function deferred() {
var reject;
var resolve;
Expand Down Expand Up @@ -520,6 +564,48 @@ test('indicator collectors render once on the right and open their panels', func
assert.equal(dom.findByClass(mount, 'phalcon-debugbar-body').style.display, 'none');
});

test('an indicator draws the icon that it names', function () {
var clock = dom.findByClass(renderIndicator('clock'), 'phalcon-debugbar-indicator-icon');
var cogs = dom.findByClass(renderIndicator('cogs'), 'phalcon-debugbar-indicator-icon');

assert.deepEqual(iconShapes(clock), ['CIRCLE', 'PATH']);
assert.equal(clock.children[0].getAttribute('r'), '6.5');
assert.deepEqual(iconShapes(cogs), ['G', 'G']);
});

test('the request control draws the search icon', function () {
var mount = renderPayload({
data: {history: {panel: {url: '/_debugbar/open'}}},
meta: {widgets: {history: {label: 'History', panel: 'history'}}}
});
var control = dom.findByClass(mount, 'phalcon-debugbar-request-control');
var search = dom.findByClass(control, 'phalcon-debugbar-indicator-icon');

assert.deepEqual(iconShapes(search), ['CIRCLE', 'PATH']);
assert.equal(search.children[0].getAttribute('r'), '4.5');
});

test('an indicator with an unknown icon name shows its value and no icon', function () {
['clcok', 'constructor', ''].forEach(function (name) {
var mount = renderIndicator(name);
var indicator = dom.findByClass(mount, 'phalcon-debugbar-metric');

assert.equal(dom.findByClass(indicator, 'phalcon-debugbar-indicator-icon'), null);
assert.equal(indicator.textContent, '12.34ms');
});
});

test('the icon names in PHP match the drawings in the client', function () {
var types = fs.readFileSync(require.resolve('../../src/DebugBar/DebugBarTypes.php'), 'utf8');
var definition = /@phpstan-type icon_type (\S+)/.exec(types);

assert.ok(definition, 'DebugBarTypes must define icon_type');
assert.deepEqual(
definition[1].replace(/'/g, '').split('|').sort(),
debugbar.iconNames.slice().sort()
);
});

test('the client renders the output of the real collectors', function () {
var dataNode = new dom.TestElement('script');
var mount = new dom.TestElement('div');
Expand Down
Loading