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
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,10 @@
"APIMethod": "stub",
"proxyURL": "http://localhost:8000",
"scripts": {
"test": "NODE_ENV=test node_modules/.bin/jest tests/js/spec",
"test-ci": "NODE_ENV=test node_modules/.bin/jest tests/js/spec --runInBand",
"test-watch": "NODE_ENV=test node_modules/.bin/jest tests/js/spec --watch",
"test": "npm run jest -- tests/js/spec",
"test-ci": "npm run test -- --runInBand",
"test-watch": "npm run test -- --watch",
"jest": "NODE_ENV=test node_modules/.bin/jest",
"lint": "node_modules/.bin/eslint tests/js src/sentry/static/sentry/app --ext .jsx"
},
"jest": {
Expand Down
42 changes: 42 additions & 0 deletions src/sentry/static/sentry/app/components/QueryCount.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React, {PropTypes} from 'react';
import classNames from 'classnames';

/**
* Displays a number count. If `max` is specified, then give representation
* of count, i.e. "1000+"
*
* Render nothing by default if `count` is falsy.
*/
function QueryCount({className, count, max, hideIfEmpty, inline}) {
const countOrMax = typeof max !== 'undefined' && count >= max ? `${max}+` : count;
const cx = classNames('query-count', className, {
inline
});

if (hideIfEmpty && !count) {
return null;
}

return (
<div className={cx}>
<span>(</span>
<span className="query-count-value">
{countOrMax}
</span>
<span>)</span>
</div>
);
}
QueryCount.propTypes = {
className: PropTypes.string,
count: PropTypes.number,
max: PropTypes.number,
hideIfEmpty: PropTypes.bool,
inline: PropTypes.bool
};
QueryCount.defaultProps = {
hideIfEmpty: true,
inline: true
};

export default QueryCount;
13 changes: 13 additions & 0 deletions src/sentry/static/sentry/app/views/stream.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ const Stream = React.createClass({
statsPeriod: this.props.defaultStatsPeriod,
realtimeActive,
pageLinks: '',
queryCount: null,
dataLoading: true,
error: false,
query: '',
Expand Down Expand Up @@ -325,6 +326,7 @@ const Stream = React.createClass({

this.setState({
dataLoading: true,
queryCount: null,
error: false
});

Expand Down Expand Up @@ -372,9 +374,18 @@ const Stream = React.createClass({

this._streamManager.push(data);

let queryCount = jqXHR.getResponseHeader('X-Hits');
let queryMaxCount = jqXHR.getResponseHeader('X-Max-Hits');

return void this.setState({
error: false,
dataLoading: false,
queryCount: typeof queryCount !== 'undefined'
? parseInt(queryCount, 10) || 0
: 0,
queryMaxCount: typeof queryMaxCount !== 'undefined'
? parseInt(queryMaxCount, 10) || 0
: 0,
pageLinks: jqXHR.getResponseHeader('Link')
});
},
Expand Down Expand Up @@ -703,6 +714,8 @@ const Stream = React.createClass({
sort={this.state.sort}
tags={this.state.tags}
searchId={searchId}
queryCount={this.state.queryCount}
queryMaxCount={this.state.queryMaxCount}
defaultQuery={this.props.defaultQuery}
onSortChange={this.onSortChange}
onSearch={this.onSearch}
Expand Down
44 changes: 33 additions & 11 deletions src/sentry/static/sentry/app/views/stream/filters.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ const StreamFilters = React.createClass({
filter: React.PropTypes.string,
query: React.PropTypes.string,
isSearchDisabled: React.PropTypes.bool,
queryCount: React.PropTypes.number,
queryMaxCount: React.PropTypes.number,

onSortChange: React.PropTypes.func,
onSearch: React.PropTypes.func,
Expand All @@ -44,7 +46,25 @@ const StreamFilters = React.createClass({
},

render() {
let {access, orgId, projectId, searchId} = this.props;
let {
access,
orgId,
projectId,
searchId,
queryCount,
queryMaxCount,
query,
savedSearchList,
tags,
defaultQuery,
isSearchDisabled,
sort,

onSidebarToggle,
onSearch,
onSavedSearchCreate,
onSortChange
} = this.props;

return (
<div className="stream-header">
Expand All @@ -55,31 +75,33 @@ const StreamFilters = React.createClass({
orgId={orgId}
projectId={projectId}
searchId={searchId}
query={this.props.query}
onSavedSearchCreate={this.props.onSavedSearchCreate}
savedSearchList={this.props.savedSearchList}
queryCount={queryCount}
queryMaxCount={queryMaxCount}
query={query}
onSavedSearchCreate={onSavedSearchCreate}
savedSearchList={savedSearchList}
/>
</div>
<div className="col-sm-7">
<div className="search-container">
<div className="stream-sort">
<SortOptions sort={this.props.sort} onSelect={this.props.onSortChange} />
<SortOptions sort={sort} onSelect={onSortChange} />
</div>

<SearchBar
orgId={orgId}
projectId={projectId}
ref="searchBar"
tags={this.props.tags}
defaultQuery={this.props.defaultQuery || ''}
tags={tags}
defaultQuery={defaultQuery || ''}
placeholder={t('Search for events, users, tags, and everything else.')}
query={this.props.query || ''}
onSearch={this.props.onSearch}
disabled={this.props.isSearchDisabled}
query={query || ''}
onSearch={onSearch}
disabled={isSearchDisabled}
/>
<a
className="btn btn-default toggle-stream-sidebar"
onClick={this.props.onSidebarToggle}>
onClick={onSidebarToggle}>
<span className="icon-filter" />
</a>
</div>
Expand Down
17 changes: 13 additions & 4 deletions src/sentry/static/sentry/app/views/stream/savedSearchSelector.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import React from 'react';
import Modal from 'react-bootstrap/lib/Modal';
import {Link} from 'react-router';

import {t} from '../../locale';
import ApiMixin from '../../mixins/apiMixin';
import DropdownLink from '../../components/dropdownLink';
import IndicatorStore from '../../stores/indicatorStore';
import DropdownLink from '../../components/dropdownLink';
import QueryCount from '../../components/QueryCount';
import MenuItem from '../../components/menuItem';
import {t} from '../../locale';
import {BooleanField, FormState, TextField} from '../../components/forms';

const SaveSearchButton = React.createClass({
Expand Down Expand Up @@ -186,6 +187,8 @@ const SavedSearchSelector = React.createClass({
searchId: React.PropTypes.string,
access: React.PropTypes.object.isRequired,
savedSearchList: React.PropTypes.array.isRequired,
queryCount: React.PropTypes.number,
queryMaxCount: React.PropTypes.number,
onSavedSearchCreate: React.PropTypes.func.isRequired
},

Expand All @@ -201,7 +204,7 @@ const SavedSearchSelector = React.createClass({
},

render() {
let {access, orgId, projectId} = this.props;
let {access, orgId, projectId, queryCount, queryMaxCount} = this.props;
let children = this.props.savedSearchList.map(search => {
// TODO(dcramer): we want these to link directly to the saved
// search ID, and pass that into the backend (probably)
Expand All @@ -214,7 +217,13 @@ const SavedSearchSelector = React.createClass({
});
return (
<div className="saved-search-selector">
<DropdownLink title={this.getTitle()}>
<DropdownLink
title={
<span>
<span>{this.getTitle()}</span>
<QueryCount count={queryCount} max={queryMaxCount} />
</span>
}>
{children.length
? children
: <li className="empty">
Expand Down
7 changes: 7 additions & 0 deletions src/sentry/static/sentry/less/QueryCount.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.query-count {
margin-left: 4px;

&.inline {
display: inline-block;
}
}
1 change: 1 addition & 0 deletions src/sentry/static/sentry/less/sentry.less
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,4 @@
@import url("./setup-wizard.less");
@import url("./spacing.less");
@import url("./stream.less");
@import url("./QueryCount.less");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already merged, but can we keep to the same camelCase naming scheme we're rolling going forward? (Until such a time as we changed it globally.)

30 changes: 30 additions & 0 deletions tests/js/spec/components/QueryCount.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import {shallow} from 'enzyme';
import QueryCount from 'app/components/QueryCount';
import toJson from 'enzyme-to-json';

describe('QueryCount', function() {
it('displays count when no max', function() {
const wrapper = shallow(<QueryCount count={5} />);
expect(toJson(wrapper)).toMatchSnapshot();
});
it('displays count when count < max', function() {
const wrapper = shallow(<QueryCount count={5} max={500} />);
expect(toJson(wrapper)).toMatchSnapshot();
});

it('does not render if count is 0', function() {
const wrapper = shallow(<QueryCount count={0} />);
expect(toJson(wrapper)).toMatchSnapshot();
});

it('can render when count is 0 when `hideIfEmpty` is false', function() {
const wrapper = shallow(<QueryCount count={0} hideIfEmpty={false} />);
expect(toJson(wrapper)).toMatchSnapshot();
});

it('displays max count if count >= max', function() {
const wrapper = shallow(<QueryCount count={500} max={500} />);
expect(toJson(wrapper)).toMatchSnapshot();
});
});
75 changes: 75 additions & 0 deletions tests/js/spec/components/__snapshots__/QueryCount.spec.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`QueryCount can render when count is 0 when \`hideIfEmpty\` is false 1`] = `
<div
className="query-count inline"
>
<span>
(
</span>
<span
className="query-count-value"
>
0
</span>
<span>
)
</span>
</div>
`;

exports[`QueryCount displays count when count < max 1`] = `
<div
className="query-count inline"
>
<span>
(
</span>
<span
className="query-count-value"
>
5
</span>
<span>
)
</span>
</div>
`;

exports[`QueryCount displays count when no max 1`] = `
<div
className="query-count inline"
>
<span>
(
</span>
<span
className="query-count-value"
>
5
</span>
<span>
)
</span>
</div>
`;

exports[`QueryCount displays max count if count >= max 1`] = `
<div
className="query-count inline"
>
<span>
(
</span>
<span
className="query-count-value"
>
500+
</span>
<span>
)
</span>
</div>
`;

exports[`QueryCount does not render if count is 0 1`] = `null`;