Merge pull request #510 from ing-bank/feat/sb-logger

Feat/sb logger
This commit is contained in:
Thijs Louisse 2020-01-21 17:08:03 +01:00 committed by GitHub
commit 0c027baf4d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 215 additions and 74 deletions

View file

@ -12,7 +12,7 @@ module.exports = config => {
// npm run test -- --grep test/foo/bar.test.js
// 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',
},
],

View file

@ -10,6 +10,12 @@
"type": "String",
"description": "The title of action logger",
"default": "Action Logger"
},
{
"name": "simple",
"type": "Boolean",
"description": "Simple mode, which only shows a single log",
"default": "false"
}
],
"events": [],

View file

@ -6,6 +6,7 @@ export class SbActionLogger extends LitElement {
static get properties() {
return {
title: { type: String, reflect: true },
simple: { type: Boolean, reflect: true },
__logCounter: { type: Number },
};
}
@ -21,6 +22,8 @@ export class SbActionLogger extends LitElement {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
display: block;
font-family: 'Nunito Sans', -apple-system, '.SFNSText-Regular', 'San Francisco',
BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
.header__info {
@ -88,6 +91,7 @@ export class SbActionLogger extends LitElement {
.logger__log {
padding: 16px;
display: flex;
}
.logger__log:not(:last-child) {
@ -102,6 +106,16 @@ export class SbActionLogger extends LitElement {
white-space: -o-pre-wrap; /* Opera 7 */
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 +125,10 @@ export class SbActionLogger extends LitElement {
this.__logCounter = 0;
}
get loggerEl() {
return this.shadowRoot.querySelector('.logger');
}
/**
* Renders the passed content as a node, and appends it to the logger
* Only supports simple values, will be interpreted to a String
@ -119,14 +137,20 @@ export class SbActionLogger extends LitElement {
* @param {} content Content to be logged to the action logger
*/
log(content) {
const loggerEl = this.shadowRoot.querySelector('.logger');
const offlineRenderContainer = document.createElement('div');
render(this._logTemplate(content), offlineRenderContainer);
// TODO: Feature, combine duplicate consecutive logs as 1 dom element and add a counter for dupes
loggerEl.appendChild(offlineRenderContainer.firstElementChild);
this.__logCounter += 1;
this.__animateCue();
loggerEl.scrollTo({ top: loggerEl.scrollHeight, behavior: 'smooth' });
if (this.simple) {
this.__clearLogs();
}
if (this.__isConsecutiveDuplicateLog(content)) {
this.__handleConsecutiveDuplicateLog();
} else {
this.__appendLog(content);
this.loggerEl.scrollTo({ top: this.loggerEl.scrollHeight, behavior: 'smooth' });
}
this.__logCounter += 1; // increment total log counter
}
/**
@ -143,6 +167,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() {
const cueEl = this.shadowRoot.querySelector('.header__log-cue-overlay');
cueEl.classList.remove('header__log-cue-overlay--slide');
@ -159,20 +237,4 @@ export class SbActionLogger extends LitElement {
loggerEl.innerHTML = '';
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

@ -21,37 +21,50 @@ import '../../sb-action-logger.js';
A visual element to show action logs in Storybook demos `sb-action-logger`
<Story name="Default">
{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>
<button
@click=${() => document.getElementById('logger-a53209ghj').log('Hello, World!')}
>Click this button</button>
<sb-action-logger id="logger-a53209ghj"></sb-action-logger>
`}
{() => {
const uid = Math.random().toString(36).substr(2, 10);
return html`
<div>To log: <code>Hello, World!</code></div>
<button
@click=${() => {
document.getElementById(`logger-${uid}`).log('Hello, World!');
}}
>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>
`;
}}
</Story>
You need some reference to your logger. Above example shows this by using a unique ID.
```js
const uid = Math.random().toString(36).substr(2, 10);
```
This connects the logger element to the trigger.
```html
<div>To log: <code>Hello, World!</code></div>
<button
@click=${() => document.getElementById('logger-a53209ghj').log('Hello, World!')}
@click=${() => document.getElementById('logger-${uid}').log('Hello, World!')}
>Click this button</button>
<sb-action-logger id="logger-a53209ghj"></sb-action-logger>
<sb-action-logger id="logger-${uid}"></sb-action-logger>
```
Note that you need some reference to your logger. Above example shows this by using a unique ID.
## Features:
- A public method `log` to log things to the action logger.
- Overridable `title` property.
- Clear button to clear logs
- A counter to count the total amount of logs
- Stacks consecutive duplicate logs and shows a counter
- `simple` property/attribute to only show a single log
## How to use
@ -67,17 +80,55 @@ npm i sb-action-logger
## Variations
### Custom Title
### Simple mode
<Story name="Custom Title">
{html`
<button
@click=${() => document.getElementById('logger-vnfoiu3478').log('Hello, World!')}
>Log</button>
<sb-action-logger id="logger-vnfoiu3478" .title=${'Hello World'}></sb-action-logger>
`}
Simple mode essentially means there is only ever 1 log.
Duplicates are not counted or stacked, but you will still see the visual cue.
<Story name="Simple mode">
{() => {
const uid = Math.random().toString(36).substr(2, 10);
return html`
<div>To log: <code>Hello, World!</code></div>
<button
@click=${() => {
document.getElementById(`logger-${uid}`).log('Hello, World!');
}}
>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 simple id="logger-${uid}"></sb-action-logger>
`;
}}
</Story>
```html
<sb-action-logger simple></sb-action-logger>
```
### Custom Title
You can customize the action logger title with the `.title` property.
<Story name="Custom Title">
{() => {
const uid = Math.random().toString(36).substr(2, 10);
return html`
<button
@click=${() => document.getElementById(`logger-${uid}`).log('Hello, World!')}
>Log</button>
<sb-action-logger id="logger-${uid}" .title=${'Hello World'}></sb-action-logger>
`;
}}
</Story>
```html
<sb-action-logger .title=${'Hello World'}></sb-action-logger>
```
## Rationale
@ -101,16 +152,16 @@ Maybe in the future I will abstract this component to a more generic (ugly) one
If you use an action logger inside your Story in Storybook, you will also see it in your canvas, and this may not be your intention.
One idea I have is that we can simplify the usage further by making this a Storybook (docs-)plugin or decorator or whatever.
I am not too familiar with them at the moment, but it would be cool if someone can simply enable an action logger option on a particular Story inside their .mdx,
and then actions are automatically logged to the visual logger below it. Would need to figure out how to catch the action and pass it to the visual logger element.
One idea is to simplify the usage further by making this a Storybook (docs-)plugin or decorator or whatever.
It would be cool if someone can simply enable an action logger option on a particular Story inside their .mdx,
and then actions are automatically logged to the visual logger below it.
Would need to figure out how to catch the action and pass it to the visual logger element.
I have not investigated yet on the how, but that is the rough idea. Feel free to help out here :)
Isn't investigated yet on the how, but that is the rough idea.
## Future
I plan on adding more features.
They can always be found in the test folder where I specify new features as tests first, and then I skip them until I implement them. Easy to find them that way.
If the feature you'd like is not in the tests, I probably did not think about it yet or did not plan to do it yet, so in that case feel free to make an issue so we can add it.
New planned features can be found in the test folder where they are specified as skipped tests.
If the feature you'd like is not in the tests, feel free to make an issue so we can add it.
I'm happy to accept pull requests for skipped tests (features to be added), see the CONTRIBUTING.md on GitHub for more details on how to contribute to this codebase.
See our CONTRIBUTING.md for more guidelines on how to contribute.

View file

@ -91,30 +91,52 @@ describe('sb-action-logger', () => {
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`
<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');
});
it('can be set to simple mode for only showing a single log statement', async () => {
const el = await fixture(html`
<sb-action-logger simple></sb-action-logger>
`);
el.log('Hello, World!');
const loggerEl = el.shadowRoot.querySelector('.logger');
expect(loggerEl.children.length).to.equal(1);
expect(loggerEl.firstElementChild.querySelector('code').innerText).to.equal('Hello, World!');
el.log('Hello, Earth!');
expect(loggerEl.children.length).to.equal(1);
expect(loggerEl.firstElementChild.querySelector('code').innerText).to.equal('Hello, Earth!');
el.log('Hello, Planet!');
el.log('Hello, Planet!');
expect(loggerEl.children.length).to.equal(1);
expect(loggerEl.firstElementChild.querySelector('code').innerText).to.equal('Hello, Planet!');
expect(loggerEl.firstElementChild.querySelector('.logger__log-count')).to.be.null;
});
});
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
it.skip('can be set to mode=simple for only showing a single log statement', async () => {
const el = await fixture(html`
<sb-action-logger simple></sb-action-logger>
`);
expect(el).to.be.true;
});
it.skip('fires a sb-action-logged event when something is logged to the logger', async () => {
const el = await fixture(html`
<sb-action-logger></sb-action-logger>