diff --git a/app/components/Checkbox.js b/app/components/Checkbox.js index 6f17e645e..33536cf83 100644 --- a/app/components/Checkbox.js +++ b/app/components/Checkbox.js @@ -8,15 +8,18 @@ export type Props = { label?: string, className?: string, note?: string, + small?: boolean, }; const LabelText = styled.span` font-weight: 500; - margin-left: 10px; + margin-left: ${props => (props.small ? '6px' : '10px')}; + ${props => (props.small ? `color: ${props.theme.textSecondary}` : '')}; `; const Wrapper = styled.div` padding-bottom: 8px; + ${props => (props.small ? 'font-size: 14px' : '')}; `; const Label = styled.label` @@ -28,15 +31,16 @@ export default function Checkbox({ label, note, className, + small, short, ...rest }: Props) { return ( - + {note && {note}} diff --git a/app/scenes/Search/Search.js b/app/scenes/Search/Search.js index 6bff9f7e6..63e1c2770 100644 --- a/app/scenes/Search/Search.js +++ b/app/scenes/Search/Search.js @@ -18,6 +18,8 @@ import { meta } from 'utils/keyboard'; import Flex from 'shared/components/Flex'; import Empty from 'components/Empty'; import Fade from 'components/Fade'; +import Checkbox from 'components/Checkbox'; + import HelpText from 'components/HelpText'; import CenteredContent from 'components/CenteredContent'; import LoadingIndicator from 'components/LoadingIndicator'; @@ -33,33 +35,6 @@ type Props = { notFound: ?boolean, }; -const Container = styled(CenteredContent)` - > div { - position: relative; - height: 100%; - } -`; - -const ResultsWrapper = styled(Flex)` - position: absolute; - transition: all 300ms cubic-bezier(0.65, 0.05, 0.36, 1); - top: ${props => (props.pinToTop ? '0%' : '50%')}; - margin-top: ${props => (props.pinToTop ? '40px' : '-75px')}; - width: 100%; -`; - -const ResultList = styled(Flex)` - margin-bottom: 150px; - opacity: ${props => (props.visible ? '1' : '0')}; - transition: all 400ms cubic-bezier(0.65, 0.05, 0.36, 1); -`; - -const StyledArrowKeyNavigation = styled(ArrowKeyNavigation)` - display: flex; - flex-direction: column; - flex: 1; -`; - @observer class Search extends React.Component { firstDocument: ?DocumentPreview; @@ -68,6 +43,7 @@ class Search extends React.Component { @observable offset: number = 0; @observable allowLoadMore: boolean = true; @observable isFetching: boolean = false; + @observable includeArchived: boolean = false; @observable pinToTop: boolean = !!this.props.match.params.query; componentDidMount() { @@ -114,6 +90,11 @@ class Search extends React.Component { this.fetchResultsDebounced(); }; + handleFilterChange = ev => { + this.includeArchived = ev.target.checked; + this.fetchResultsDebounced(); + }; + @action loadMoreResults = async () => { // Don't paginate if there aren't more results or we’re in the middle of fetching @@ -132,6 +113,7 @@ class Search extends React.Component { const results = await this.props.documents.search(this.query, { offset: this.offset, limit: DEFAULT_PAGINATION_LIMIT, + includeArchived: this.includeArchived, }); if (results.length > 0) this.pinToTop = true; @@ -199,6 +181,17 @@ class Search extends React.Component { )} + {this.pinToTop && ( + + + + )} {showEmpty && No matching documents.} { } } +const Container = styled(CenteredContent)` + > div { + position: relative; + height: 100%; + } +`; + +const ResultsWrapper = styled(Flex)` + position: absolute; + transition: all 300ms cubic-bezier(0.65, 0.05, 0.36, 1); + top: ${props => (props.pinToTop ? '0%' : '50%')}; + margin-top: ${props => (props.pinToTop ? '40px' : '-75px')}; + width: 100%; +`; + +const ResultList = styled(Flex)` + margin-bottom: 150px; + opacity: ${props => (props.visible ? '1' : '0')}; + transition: all 400ms cubic-bezier(0.65, 0.05, 0.36, 1); +`; + +const StyledArrowKeyNavigation = styled(ArrowKeyNavigation)` + display: flex; + flex-direction: column; + flex: 1; +`; + +const Filters = styled(Flex)` + border-bottom: 1px solid ${props => props.theme.divider}; + margin-bottom: 10px; +`; + export default withRouter(inject('documents')(Search)); diff --git a/server/api/documents.js b/server/api/documents.js index 0643d984a..883484cf0 100644 --- a/server/api/documents.js +++ b/server/api/documents.js @@ -367,12 +367,13 @@ router.post('documents.restore', auth(), async ctx => { }); router.post('documents.search', auth(), pagination(), async ctx => { - const { query } = ctx.body; + const { query, includeArchived } = ctx.body; const { offset, limit } = ctx.state.pagination; ctx.assertPresent(query, 'query is required'); const user = ctx.state.user; const results = await Document.searchForUser(user, query, { + includeArchived: includeArchived === 'true', offset, limit, }); diff --git a/server/api/documents.test.js b/server/api/documents.test.js index 1dac998d3..392a24bce 100644 --- a/server/api/documents.test.js +++ b/server/api/documents.test.js @@ -464,6 +464,29 @@ describe('#documents.search', async () => { expect(body.data.length).toEqual(0); }); + it('should return archived documents if chosen', async () => { + const { user } = await seed(); + const document = await buildDocument({ + title: 'search term', + text: 'search term', + teamId: user.teamId, + }); + await document.archive(user.id); + + const res = await server.post('/api/documents.search', { + body: { + token: user.getJwtToken(), + query: 'search term', + includeArchived: 'true', + }, + }); + const body = await res.json(); + + expect(res.status).toEqual(200); + expect(body.data.length).toEqual(1); + expect(body.data[0].document.text).toEqual('search term'); + }); + it('should not return documents in private collections not a member of', async () => { const { user } = await seed(); const collection = await buildCollection({ private: true }); diff --git a/server/models/Document.js b/server/models/Document.js index 8ee1e1cc3..7b8a11fdc 100644 --- a/server/models/Document.js +++ b/server/models/Document.js @@ -224,7 +224,7 @@ Document.searchForUser = async ( FROM documents WHERE "searchVector" @@ to_tsquery('english', :query) AND "collectionId" IN(:collectionIds) AND - "archivedAt" IS NULL AND + ${options.includeArchived ? '' : '"archivedAt" IS NULL AND'} "deletedAt" IS NULL AND ("publishedAt" IS NOT NULL OR "createdById" = '${user.id}') ORDER BY diff --git a/server/pages/developers/Api.js b/server/pages/developers/Api.js index 5717a4367..5c88c7ee5 100644 --- a/server/pages/developers/Api.js +++ b/server/pages/developers/Api.js @@ -250,11 +250,13 @@ export default function Pricing() { - This methods allows you to search all of your documents with - keywords. + This methods allows you to search your teams documents with + keywords. Search results will be restricted to those accessible by + the current access token. +