Skip to content
Open
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
60 changes: 53 additions & 7 deletions src/components/MiniSearchUI.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,57 @@ import NavbarSearch from '../components/NavbarSearch';
import SearchResults from '../components/SearchResults';
import Scrollable from '../components/Scrollable';
import SearchResultsCount from '../components/SearchResultsCount';
import QueryResponse from '../api/QueryResponse';

type MiniSearchUIProps = {
/**
* A scale (1.0 = 100%) to use when rendering the search results part of
* the MiniSearchUI. Optional—defaults to 100%.
*/
scale: number;
/**
* Optional callback to handle searching if not using the context's
* Searcher component to do the searching (otherwise the searcher's onSearch method is used).
*/
onSearch?: (q: string) => void;
/**
* Optional callback to handle updating the query string if not using the context's
* Searcher component to do the searching (otherwise the searcher's onSearch method is used).
*/
updateQuery?: (q: string) => void;
/**
* Optional query response to render if not using the context's
* Searcher component to do the searching (otherwise the searcher's state's response is used).
*/
response?: QueryResponse | null;
/**
* Optional query error to render if not using the context's
* Searcher component to do the searching (otherwise the searcher's state's error is used).
*/
error?: string | null;
};

type MiniSearchUIDefaultProps = {
scale: number;
response: null;
error: null;
};

type MiniSearchUIState = {
query: string;
}

/**
* A miniature, self-contained component that presents super simple search UI including a text field for the
* query, an indication of the number or results or error from the query, and a small, scrollable results area
* showing the resulting documents. It must be nested inside a Searcher component and will use that parent
* Searcher to manage its state.
*/
export default class MiniSearchUI extends React.Component<MiniSearchUIDefaultProps, MiniSearchUIProps, void> {
export default class MiniSearchUI extends React.Component<MiniSearchUIDefaultProps, MiniSearchUIProps, MiniSearchUIState> {
static defaultProps = {
scale: 1.0,
response: null,
error: null,
};

static contextTypes = {
Expand All @@ -39,18 +68,30 @@ export default class MiniSearchUI extends React.Component<MiniSearchUIDefaultPro

constructor(props: MiniSearchUIProps) {
super(props);
this.state = {
query: '*',
};
(this: any).doSearch = this.doSearch.bind(this);
(this: any).updateSearchQuery = this.updateSearchQuery.bind(this);
}

doSearch() {
const searcher = this.context.searcher;
searcher.doSearch();
if (this.props.onSearch) {
this.props.onSearch(this.state.query);
} else {
const searcher = this.context.searcher;
searcher.doSearch();
}
}

updateSearchQuery(query: string) {
const searcher = this.context.searcher;
searcher.updateQuery(query);
if (this.props.updateQuery) {
this.props.updateQuery(query);
} else {
const searcher = this.context.searcher;
searcher.updateQuery(query);
}
this.setState({ query });
}

render() {
Expand All @@ -59,12 +100,16 @@ export default class MiniSearchUI extends React.Component<MiniSearchUIDefaultPro
<NavbarSearch
onSearch={this.doSearch}
updateSearchString={this.updateSearchQuery}
value={this.context.searcher.state.query}
value={this.state.query}
style={{
marginLeft: '8px',
}}
/>
<SearchResultsCount style={{ marginLeft: '20px', paddingBottom: '8px' }} />
<SearchResultsCount
style={{ marginLeft: '20px', paddingBottom: '8px' }}
response={this.props.response}
error={this.props.error}
/>
<Scrollable
style={{
height: '428px',
Expand All @@ -79,6 +124,7 @@ export default class MiniSearchUI extends React.Component<MiniSearchUIDefaultPro
style={{
transform: `scale(${this.props.scale}, ${this.props.scale})`,
}}
response={this.props.response}
/>
</Scrollable>
</div>
Expand Down
15 changes: 13 additions & 2 deletions src/components/SearchResults.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ type SearchResultsProps = {
hide360Link?: boolean;
/** A style to apply to the results list */
style: any;
/**
* A response can be passed in from custom searches if you don't want
* to use the response on the searcher in this.context
*/
response?: QueryResponse | null;
/** Offset of search results for long scrolling or pagination */
offset?: number;
};

type SearchResultsDefaultProps = {
Expand All @@ -67,6 +74,8 @@ type SearchResultsDefaultProps = {
showScores: boolean;
showTags: boolean;
showRatings: boolean;
response: null;
offset: number;
style: any;
};

Expand All @@ -81,6 +90,8 @@ export default class SearchResults extends React.Component<SearchResultsDefaultP
showScores: false,
showTags: true,
showRatings: true,
response: null,
offset: 0,
hide360Link: false,
style: {},
};
Expand All @@ -99,8 +110,8 @@ export default class SearchResults extends React.Component<SearchResultsDefaultP
} = this.props;

const { searcher = null } = this.context;
const response = searcher && searcher.state ? searcher.state.response : null;
const offset = searcher && searcher.state ? searcher.state.resultsOffset : null;
const response = this.props.response !== null ? this.props.response : searcher.state.response;
const offset = this.props.response && this.props.offset ? this.props.offset : searcher.state.resultsOffset;

let formatRenderers: Array<SearchResultRenderer> = [];
if (searcher && searcher.state && searcher.state.debug) {
Expand Down
89 changes: 57 additions & 32 deletions src/components/SearchResultsCount.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,55 +2,80 @@
import React from 'react';
import PropTypes from 'prop-types';

type SearchResultsCountProps = {
/**
* A response can be passed in from custom searches if you don't want
* to use the response on the searcher in this.context
*/
response: QueryResponse | null;
/**
* If attempting to pass in a custom response, there may be queries that result in errors,
* which can be passed in here. Otherwise the error from the searcher
* on this.context will be used.
*/
error: string | null;
}

type SearchResultsCountDefaultProps = {
response: QueryResponse | null;
error: string | null;
}

/**
* The count of search results or an indication
* that a search hasn't yet happened.
*/
export default class SearchResultsCount extends React.Component<void, {}, void> {
export default class SearchResultsCount extends React.Component<SearchResultsCountProps, SearchResultsCountDefaultProps, void> {
static defaultProps = {
response: null,
error: null,
}

static contextTypes = {
searcher: PropTypes.any,
};

static displayName = 'SearchResultsCount';

render() {
const searcher = this.context.searcher;
let message;
renderMessage = () => {
const { response, error } = this.props;
const { searcher } = this.context;
const derivedResponse = response || searcher.state.response;

let countMessage;
if (searcher) {
const response = searcher.state.response;
if (response) {
const count = response.totalHits;
if (count === 0) {
countMessage = 'No results found';
} else if (count === 1) {
countMessage = '1 result found';
} else {
const countStr = Number(count).toLocaleString();
countMessage = `${countStr} results found`;
}
message = (
<span>
{countMessage}
<span style={{ fontWeight: 'normal' }}>
{' '}
(in {response.totalTime}ms)
</span>
</span>
);
} else if (searcher.state.error) {
// got an error...
message = `Error: ${searcher.state.error}`;

if (derivedResponse) {
const { totalHits: count } = response;
if (count === 0 || typeof count !== 'number') {
countMessage = 'No results found';
} else if (count === 1) {
countMessage = '1 result found';
} else {
message = ''; // Not yet searched...
countMessage = `${count} results found`;
}
} else {
message = 'No searcher is configured.';
return (
<span>
{countMessage}
<span style={{ fontWeight: 'normal' }}>
{' '}
(in {response.totalTime}ms)
</span>
</span>
);
} else if (error) {
return `Error: ${error}`;
} else if (searcher.state.error) {
// got an error...
return `Error: ${searcher.state.error}`;
}
// Not yet searched...
return '';
}

render() {
return (
<div className="attivio-globalmastnavbar-results">
{message}
{this.renderMessage()}
</div>
);
}
Expand Down
Binary file added styleguide/addNodeIcon.a1a2d01b.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added styleguide/backIcon.dd0baa69.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions styleguide/build/0.28beda6c.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions styleguide/build/bundle.1e6b120f.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions styleguide/build/bundle.1f7507e0.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions styleguide/build/bundle.d783eeab.js

Large diffs are not rendered by default.

Loading