feat(helpers): add handling duplicate consecutive logs to action logger

This commit is contained in:
Joren Broekema 2020-01-17 16:08:13 +01:00
parent e50b5a4ce4
commit 288c2531e6
4 changed files with 104 additions and 39 deletions

View file

@ -12,7 +12,7 @@ module.exports = config => {
// npm run test -- --grep test/foo/bar.test.js // npm run test -- --grep test/foo/bar.test.js
// npm run test -- --grep test/bar/* // npm run test -- --grep test/bar/*
{ {
pattern: config.grep ? config.grep : 'packages/*/test/**/*.test.js', pattern: config.grep ? config.grep : 'packages/**/*/test/**/*.test.js',
type: 'module', type: 'module',
}, },
], ],

View file

@ -21,6 +21,8 @@ export class SbActionLogger extends LitElement {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
display: block; display: block;
font-family: 'Nunito Sans', -apple-system, '.SFNSText-Regular', 'San Francisco',
BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Helvetica, Arial, sans-serif;
} }
.header__info { .header__info {
@ -88,6 +90,7 @@ export class SbActionLogger extends LitElement {
.logger__log { .logger__log {
padding: 16px; padding: 16px;
display: flex;
} }
.logger__log:not(:last-child) { .logger__log:not(:last-child) {
@ -102,6 +105,16 @@ export class SbActionLogger extends LitElement {
white-space: -o-pre-wrap; /* Opera 7 */ white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */ word-wrap: break-word; /* Internet Explorer 5.5+ */
} }
.logger__log-count {
line-height: 8px;
font-size: 12px;
padding: 4px;
border-radius: 4px;
margin-right: 8px;
color: white;
background-color: #777;
}
`; `;
} }
@ -111,6 +124,10 @@ export class SbActionLogger extends LitElement {
this.__logCounter = 0; this.__logCounter = 0;
} }
get loggerEl() {
return this.shadowRoot.querySelector('.logger');
}
/** /**
* Renders the passed content as a node, and appends it to the logger * Renders the passed content as a node, and appends it to the logger
* Only supports simple values, will be interpreted to a String * Only supports simple values, will be interpreted to a String
@ -119,14 +136,15 @@ export class SbActionLogger extends LitElement {
* @param {} content Content to be logged to the action logger * @param {} content Content to be logged to the action logger
*/ */
log(content) { log(content) {
const loggerEl = this.shadowRoot.querySelector('.logger'); if (this.__isConsecutiveDuplicateLog(content)) {
const offlineRenderContainer = document.createElement('div'); this.__handleConsecutiveDuplicateLog();
render(this._logTemplate(content), offlineRenderContainer); } else {
// TODO: Feature, combine duplicate consecutive logs as 1 dom element and add a counter for dupes this.__appendLog(content);
loggerEl.appendChild(offlineRenderContainer.firstElementChild); this.loggerEl.scrollTo({ top: this.loggerEl.scrollHeight, behavior: 'smooth' });
this.__logCounter += 1; }
this.__logCounter += 1; // increment total log counter
this.__animateCue(); this.__animateCue();
loggerEl.scrollTo({ top: loggerEl.scrollHeight, behavior: 'smooth' });
} }
/** /**
@ -143,6 +161,60 @@ export class SbActionLogger extends LitElement {
`; `;
} }
render() {
return html`
<div class="header">
<div class="header__info">
<p class="header__title">${this.title}</p>
<div class="header__counter">${this.__logCounter}</div>
<button class="header__clear" @click=${this.__clearLogs}>Clear</button>
</div>
<div class="header__log-cue">
<div class="header__log-cue-overlay"></div>
</div>
</div>
<div class="logger"></div>
`;
}
__appendLog(content) {
const offlineRenderContainer = document.createElement('div');
render(this._logTemplate(content), offlineRenderContainer);
this.loggerEl.appendChild(offlineRenderContainer.firstElementChild);
}
__isConsecutiveDuplicateLog(content) {
if (
this.loggerEl.lastElementChild &&
this.loggerEl.lastElementChild.querySelector('code').textContent.trim() === content
) {
return true;
}
return false;
}
__handleConsecutiveDuplicateLog() {
if (!this.loggerEl.lastElementChild.querySelector('.logger__log-count')) {
this.__prependLogCounterElement();
}
// Increment log counter for these duplicate logs
const logCounter = this.loggerEl.lastElementChild.querySelector('.logger__log-count');
let incrementedLogCount = logCounter.textContent;
incrementedLogCount = parseInt(incrementedLogCount, 10) + 1;
logCounter.innerText = incrementedLogCount;
}
__prependLogCounterElement() {
const countEl = document.createElement('div');
countEl.classList.add('logger__log-count');
countEl.innerText = 1;
this.loggerEl.lastElementChild.insertBefore(
countEl,
this.loggerEl.lastElementChild.firstElementChild,
);
}
__animateCue() { __animateCue() {
const cueEl = this.shadowRoot.querySelector('.header__log-cue-overlay'); const cueEl = this.shadowRoot.querySelector('.header__log-cue-overlay');
cueEl.classList.remove('header__log-cue-overlay--slide'); cueEl.classList.remove('header__log-cue-overlay--slide');
@ -159,20 +231,4 @@ export class SbActionLogger extends LitElement {
loggerEl.innerHTML = ''; loggerEl.innerHTML = '';
this.__logCounter = 0; this.__logCounter = 0;
} }
render() {
return html`
<div class="header">
<div class="header__info">
<p class="header__title">${this.title}</p>
<div class="header__counter">${this.__logCounter}</div>
<button class="header__clear" @click=${this.__clearLogs}>Clear</button>
</div>
<div class="header__log-cue">
<div class="header__log-cue-overlay"></div>
</div>
</div>
<div class="logger"></div>
`;
}
} }

View file

@ -24,18 +24,18 @@ A visual element to show action logs in Storybook demos `sb-action-logger`
{() => { {() => {
const uid = Math.random().toString(36).substr(2, 10); const uid = Math.random().toString(36).substr(2, 10);
return html` return html`
<style>
sb-action-logger {
font-family: 'Nunito Sans', -apple-system, '.SFNSText-Regular', 'San Francisco',
BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
</style>
<div>To log: <code>Hello, World!</code></div> <div>To log: <code>Hello, World!</code></div>
<button <button
@click=${() => { @click=${() => {
document.getElementById(`logger-${uid}`).log('Hello, World!'); document.getElementById(`logger-${uid}`).log('Hello, World!');
}} }}
>Click this button</button> >Click this button</button>
<div>Or to log: <code>What's up, Planet!</code></div>
<button
@click=${() => {
document.getElementById(`logger-${uid}`).log(`What's up, Planet!`);
}}
>Click this button</button>
<sb-action-logger id="logger-${uid}"></sb-action-logger> <sb-action-logger id="logger-${uid}"></sb-action-logger>
`; `;
}} }}

View file

@ -91,22 +91,31 @@ describe('sb-action-logger', () => {
expect(el.shadowRoot.querySelector('.logger').children.length).to.equal(0); expect(el.shadowRoot.querySelector('.logger').children.length).to.equal(0);
}); });
it('duplicate consecutive logs are kept as one', async () => { it('duplicate consecutive logs are kept as one, adds a visual counter', async () => {
const el = await fixture(html` const el = await fixture(html`
<sb-action-logger></sb-action-logger> <sb-action-logger></sb-action-logger>
`); `);
expect(el).to.be.true;
el.log('Hello, World!');
el.log('Hello, World!');
el.log('Hello, World!'); // 3 consecutive dupes
el.log('Hello, Earth!');
el.log('Hello, World!');
el.log('Hello, Planet!');
el.log('Hello, Planet!'); // 2 consecutive dupes
const loggerEl = el.shadowRoot.querySelector('.logger');
const firstLog = loggerEl.firstElementChild;
const lastLog = loggerEl.lastElementChild;
expect(loggerEl.children.length).to.equal(4);
expect(firstLog.querySelector('.logger__log-count').innerText).to.equal('3');
expect(lastLog.querySelector('.logger__log-count').innerText).to.equal('2');
}); });
}); });
describe('Potential Additional Features', () => { describe('Potential Additional Features', () => {
it.skip('duplicate consecutive adds a visual counter to count per duplicate', async () => {
const el = await fixture(html`
<sb-action-logger></sb-action-logger>
`);
expect(el).to.be.true;
});
// This is handy if you don't want to keep track of updates // This is handy if you don't want to keep track of updates
it.skip('can be set to mode=simple for only showing a single log statement', async () => { it.skip('can be set to mode=simple for only showing a single log statement', async () => {
const el = await fixture(html` const el = await fixture(html`