Skip to content

Commit

Permalink
Feature/quick summary filters (#138)
Browse files Browse the repository at this point in the history
* Add `toggleSingleFilter` method
Handles quick summary filtering

* Update quick summary icons to be filter buttons
Add styles for filter states

* Wire up quick summary filtering

* Make filters a property of the store

* Add intialFilterState to test

* update changelog

* Add quick summary component tests to fix coverage
  • Loading branch information
adamgruber authored Dec 13, 2019
1 parent 0cf3010 commit 027a077
Show file tree
Hide file tree
Showing 8 changed files with 226 additions and 35 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# mochawesome-report-generator changelog

## [Unreleased]
### Added
- Clicking icons in navbar enable quick filtering of single test type

## [4.0.1] / 2019-07-05
### Changed
Expand Down
9 changes: 7 additions & 2 deletions src/client/components/navbar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import styles from './navbar.css';

const cx = classNames.bind(styles);

const Navbar = ({ onMenuClick, reportTitle, stats }) => {
const Navbar = ({ onMenuClick, onQuickFilterClick, reportTitle, singleFilter, stats }) => {
const { passPercent, pendingPercent } = stats;

const failPercent = 100 - passPercent;
Expand Down Expand Up @@ -35,7 +35,10 @@ const Navbar = ({ onMenuClick, reportTitle, stats }) => {
</h1>
</div>
<div className={cx('stats')}>
<QuickSummary stats={stats} />
<QuickSummary
stats={stats}
onQuickFilterClick={onQuickFilterClick}
singleFilter={singleFilter} />
</div>
{showPctBar && (
<div className={cx('pct-bar')}>
Expand All @@ -50,7 +53,9 @@ const Navbar = ({ onMenuClick, reportTitle, stats }) => {

Navbar.propTypes = {
onMenuClick: PropTypes.func,
onQuickFilterClick: PropTypes.func,
reportTitle: PropTypes.string,
singleFilter: PropTypes.string,
stats: PropTypes.object,
};

Expand Down
35 changes: 25 additions & 10 deletions src/client/components/quick-summary/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import styles from './quick-summary.css';

const cx = classNames.bind(styles);

const QuickSummary = ({ stats }) => {
const QuickSummary = ({ onQuickFilterClick, singleFilter, stats }) => {
const {
duration,
suites,
Expand All @@ -16,6 +16,11 @@ const QuickSummary = ({ stats }) => {
pending,
skipped,
} = stats;

const filterClasses = singleFilter
? ['single-filter', `single-filter--${singleFilter.slice(4).toLowerCase()}`]
: '';

return (
<div className={cx('cnt')}>
<ul className={cx('list')}>
Expand All @@ -36,25 +41,33 @@ const QuickSummary = ({ stats }) => {
{testsRegistered}
</li>
</ul>
<ul className={cx('list')}>
<ul className={cx('list', filterClasses)}>
<li className={cx('item', 'passes')} title="Passed">
<Icon name="check" className={cx('icon', 'circle-icon')} />
{passes}
<button type="button" onClick={() => onQuickFilterClick('showPassed')}>
<Icon name="check" className={cx('icon', 'circle-icon')} />
{passes}
</button>
</li>
<li className={cx('item', 'failures')} title="Failed">
<Icon name="close" className={cx('icon', 'circle-icon')} />
{failures}
<button type="button" onClick={() => onQuickFilterClick('showFailed')}>
<Icon name="close" className={cx('icon', 'circle-icon')} />
{failures}
</button>
</li>
{!!pending && (
<li className={cx('item', 'pending')} title="Pending">
<Icon name="pause" className={cx('icon', 'circle-icon')} />
{pending}
<button type="button" onClick={() => onQuickFilterClick('showPending')}>
<Icon name="pause" className={cx('icon', 'circle-icon')} />
{pending}
</button>
</li>
)}
{!!skipped && (
<li className={cx('item', 'skipped')} title="Skipped">
<Icon name="stop" className={cx('icon', 'circle-icon')} />
{skipped}
<button type="button" onClick={() => onQuickFilterClick('showSkipped')}>
<Icon name="stop" className={cx('icon', 'circle-icon')} />
{skipped}
</button>
</li>
)}
</ul>
Expand All @@ -63,6 +76,8 @@ const QuickSummary = ({ stats }) => {
};

QuickSummary.propTypes = {
onQuickFilterClick: PropTypes.func,
singleFilter: PropTypes.string,
stats: PropTypes.object,
};

Expand Down
57 changes: 56 additions & 1 deletion src/client/components/quick-summary/quick-summary.css
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@
font-size: 16px;
flex-basis: 25%;

& button {
@extend %button-base;

display: flex;
align-items: center;
color: #fff;
cursor: pointer;

&:hover .icon {
border-color: #fff;
}
}

&.tests {
color: #fff;
}
Expand All @@ -31,27 +44,67 @@
& .icon {
color: var(--green700);
background-color: var(--green100);

@nest .single-filter & {
background-color: var(--grey300);
color: var(--grey500);
}

@nest .single-filter--passed & {
color: #fff;
background-color: var(--green700);
}
}
}

&.failures {
& .icon {
color: var(--red700);
background-color: var(--red100);

@nest .single-filter & {
background-color: var(--grey300);
color: var(--grey500);
}

@nest .single-filter--failed & {
color: #fff;
background-color: var(--red700);
}
}
}

&.pending {
& .icon {
color: var(--ltblue700);
background-color: var(--ltblue100);

@nest .single-filter & {
background-color: var(--grey300);
color: var(--grey500);
}

@nest .single-filter--pending & {
color: #fff;
background-color: var(--ltblue700);
}
}
}

&.skipped {
& .icon {
color: var(--grey700);
background-color: var(--grey100);

@nest .single-filter & {
background-color: var(--grey300);
color: var(--grey500);
}

@nest .single-filter--skipped & {
color: #fff;
background-color: var(--grey700);
}
}
}
}
Expand All @@ -67,7 +120,9 @@
.circle-icon {
font-size: 12px;
border-radius: 50%;
padding: 3px;
padding: 2px;
border: 1px solid transparent;
transition: border-color 0.2s ease-out;
}

/* Tablet 768 and up */
Expand Down
4 changes: 3 additions & 1 deletion src/client/components/report.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import 'styles/app.global.css';
import MobxDevTool from './mobxDevtool';

const MochawesomeReport = observer(props => {
const { openSideNav, reportTitle, stats, devMode, VERSION } = props.store;
const { openSideNav, toggleSingleFilter, singleFilter, reportTitle, stats, devMode, VERSION } = props.store;

return (
<Provider reportStore={props.store}>
<main>
<Navbar
onMenuClick={openSideNav}
onQuickFilterClick={toggleSingleFilter}
singleFilter={singleFilter}
reportTitle={reportTitle}
stats={stats}
/>
Expand Down
44 changes: 44 additions & 0 deletions src/client/js/reportStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ class ReportStore {
devMode: !!config.dev,
enableChart: !!config.enableCharts,
enableCode: !!config.enableCode,
filters: ['showPassed', 'showFailed', 'showPending', 'showSkipped'],
initialLoadTimeout: 300,
initialFilterState: null,
reportTitle: config.reportTitle || data.reportTitle,
results: data.results || [],
showHooksOptions: ['failed', 'always', 'never', 'context'],
Expand All @@ -29,10 +31,21 @@ class ReportStore {
showPending: config.showPending !== undefined ? config.showPending : true,
showSkipped:
config.showSkipped !== undefined ? config.showSkipped : false,
singleFilter: null,
sideNavOpen: false,
}, {
filteredSuites: observable.shallow
});

this.initialize();
}

initialize() {
// Save initial filter state so we can restore after quick filtering
this.initialFilterState = this.filters.reduce((acc, filter) => ({
...acc,
[filter]: this[filter],
}), {})
}

@action.bound
Expand All @@ -48,9 +61,34 @@ class ReportStore {
@action.bound
toggleFilter(prop) {
this.toggleIsLoading(true);
// Clear single filter prop
this.singleFilter = null;
this[prop] = !this[prop];
}

@action.bound
toggleSingleFilter(prop) {
// Not in single filter mode or changing single filter
if (this.singleFilter !== prop) {
// Set filters to false
this.filters.filter(filter => filter !== prop).forEach(filter => {
this[filter] = false;
});

// Set single filter to true
this[prop] = true;

// Update single filter prop
this.singleFilter = prop;
} else {
// Restore filters
this._restoreInitialFilterState()

// Clear single filter prop
this.singleFilter = null;
}
}

@action.bound
setShowHooks(prop) {
if (this._isValidShowHookOption(prop)) {
Expand Down Expand Up @@ -140,6 +178,12 @@ class ReportStore {
: showHooksDefault;
};

_restoreInitialFilterState = () => {
this.filters.forEach(filter => {
this[filter] = this.initialFilterState[filter];
});
};

updateFilteredSuites(timeout = this.initialLoadTimeout) {
setTimeout(() => {
this.toggleIsLoading(false);
Expand Down
23 changes: 23 additions & 0 deletions test/spec/components/quick-summary.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import { shallow } from 'enzyme';
import chai, { expect } from 'chai';
import chaiEnzyme from 'chai-enzyme';
import sinon from 'sinon';

import QuickSummary from 'components/quick-summary';

Expand All @@ -15,11 +16,14 @@ describe('<QuickSummary />', () => {
return {
wrapper,
lists: wrapper.find('.quick-summary-list'),
filterBtns: wrapper.find('button'),
};
};

beforeEach(() => {
props = {
onQuickFilterClick: sinon.spy(),
singleFilter: null,
stats: {
duration: 532,
suites: 14,
Expand Down Expand Up @@ -47,4 +51,23 @@ describe('<QuickSummary />', () => {
expect(lists.at(0).find('.quick-summary-item')).to.have.lengthOf(3);
expect(lists.at(1).find('.quick-summary-item')).to.have.lengthOf(2);
});

it('renders with `singleFilter` set', () => {
props.singleFilter = 'showPassed';
const { lists } = getInstance(props);
expect(lists.at(1)).to.have.className('single-filter');
expect(lists.at(1)).to.have.className('single-filter--passed');
});

describe('quick filters', () => {
it('should call `onQuickFilterClick` with expected argument', () => {
const { filterBtns } = getInstance(props);
const filters = ['showPassed', 'showFailed', 'showPending', 'showSkipped'];
filterBtns.forEach((btn, i) => {
btn.simulate('click');
expect(props.onQuickFilterClick.getCall(i).args[0]).to.equal(filters[i]);
})
});
});

});
Loading

0 comments on commit 027a077

Please sign in to comment.