Skip to content

Commit

Permalink
feat(archive-viewer): Add search
Browse files Browse the repository at this point in the history
  • Loading branch information
Mingze Xiao committed Dec 17, 2019
1 parent d09738f commit 41cc0a7
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 14 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"eslint-plugin-react-hooks": "^1.7.0",
"file-loader": "^4.1.0",
"fscreen": "^1.0.2",
"fuzzysearch": "^1.0.3",
"husky": "^3.0.5",
"i18n-webpack-plugin": "^1.0.0",
"jsuri": "^1.3.1",
Expand Down
68 changes: 57 additions & 11 deletions src/lib/viewers/archive/ArchiveExplorer.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import getProp from 'lodash/get';
import fuzzysearch from 'fuzzysearch';
import elementsMessages from 'box-elements-messages'; // eslint-disable-line
import intlLocaleData from 'react-intl-locale-data'; // eslint-disable-line
import Internationalize from 'box-ui-elements/es/elements/common/Internationalize';
import SearchBar from 'box-ui-elements/es/elements/common/header';
import {
readableTimeCellRenderer,
sizeCellRenderer,
Expand All @@ -18,6 +20,7 @@ import './ArchiveExplorer.scss';

const language = __LANGUAGE__; // eslint-disable-line
const { KEY_NAME, KEY_MODIFIED_AT, KEY_SIZE } = TABLE_COLUMNS;
const { VIEW_FOLDER, VIEW_SEARCH } = VIEWS;

class ArchiveExplorer extends React.Component {
static propTypes = {
Expand Down Expand Up @@ -65,7 +68,8 @@ class ArchiveExplorer extends React.Component {

this.state = {
fullPath: props.itemCollection.find(info => !info.parent).absolute_path,
view: VIEWS.VIEW_FOLDER,
searchQuery: '',
view: VIEW_FOLDER,
};
}

Expand Down Expand Up @@ -96,10 +100,11 @@ class ArchiveExplorer extends React.Component {
* @return {Object} formatted data
*/
getRowData = itemList => ({ index }) => {
const { modified_at: modifiedAt, name, size, type, ...rest } = itemList[index];
const { absolute_path: fullPath, modified_at: modifiedAt, name, size, type, ...rest } = itemList[index];

return {
[KEY_NAME]: {
fullPath,
isExternal: false,
name,
type,
Expand All @@ -112,17 +117,12 @@ class ArchiveExplorer extends React.Component {
};

/**
* Handle click event, update fullPath state
* Handle click event, update fullPath state, reset search and view
*
* @param {Object} cellValue - the cell being clicked
* @return {void}
*/
handleClick = ({ name }) => {
const { fullPath } = this.state;
this.setState({
fullPath: `${fullPath}${name}/`,
});
};
handleClick = ({ fullPath }) => this.setState({ view: VIEW_FOLDER, fullPath, searchQuery: '' });

/**
* Handle click event, update fullPath state
Expand All @@ -132,19 +132,65 @@ class ArchiveExplorer extends React.Component {
*/
handleClickFullPath = fullPath => this.setState({ fullPath });

/**
* Handle search input, update view state
*
* @param {string} query - raw query string in the search bar
* @return {void}
*/
search = query => {
const trimmedQuery = query.trim();

if (!query) {
this.setState({
searchQuery: query,
view: VIEW_FOLDER,
});
return;
}

if (!trimmedQuery) {
this.setState({
searchQuery: query,
});
return;
}

this.setState({
searchQuery: query,
view: VIEW_SEARCH,
});
};

/**
* Filter item collection for search query
*
* @param {Array<Object>} itemCollection - raw data
* @param {string} searchQuery - user input
* @return {Array<Object>} filtered items for search query
*/
getSearchResult = (itemCollection, searchQuery) => {
const trimmedQuery = searchQuery.trim();
return itemCollection.filter(item => fuzzysearch(trimmedQuery, item.name));
};

/**
* render data
*
* @return {jsx} VirtualizedTable
*/
render() {
const { itemCollection } = this.props;
const { fullPath, view } = this.state;
const itemList = this.getItemList(itemCollection, fullPath);
const { fullPath, searchQuery, view } = this.state;
const itemList =
view === VIEW_SEARCH
? this.getSearchResult(itemCollection, searchQuery)
: this.getItemList(itemCollection, fullPath);

return (
<Internationalize language={language} messages={elementsMessages}>
<div className="bp-ArchiveExplorer">
<SearchBar isSmall={false} onSearch={this.search} searchQuery={searchQuery} view={view} />
<Breadcrumbs fullPath={fullPath} onClick={this.handleClickFullPath} view={view} />
<VirtualizedTable rowData={itemList} rowGetter={this.getRowData(itemList)}>
{intl => [
Expand Down
4 changes: 4 additions & 0 deletions src/lib/viewers/archive/ArchiveExplorer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@
flex-direction: column;
width: 100%;
height: 100%;

.be-logo {
display: none;
}
}
40 changes: 37 additions & 3 deletions src/lib/viewers/archive/__tests__/ArchiveExplorer-test-react.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import { shallow } from 'enzyme';
import sinon from 'sinon';
import ArchiveExplorer from '../ArchiveExplorer';
import { TABLE_COLUMNS } from '../constants';
import { TABLE_COLUMNS, VIEWS } from '../constants';

const sandbox = sinon.sandbox.create();
let data;
Expand Down Expand Up @@ -72,6 +72,7 @@ describe('lib/viewers/archive/ArchiveExplorer', () => {
const component = shallow(<ArchiveExplorer itemCollection={data} />);

expect(component.find('.bp-ArchiveExplorer').length).to.equal(1);
expect(component.find('InjectIntl(Header)').length).to.equal(1);
expect(component.find('Breadcrumbs').length).to.equal(1);
expect(component.find('Internationalize').length).to.equal(1);
expect(component.find('InjectIntl(VirtualizedTable)').length).to.equal(1);
Expand All @@ -82,9 +83,11 @@ describe('lib/viewers/archive/ArchiveExplorer', () => {
it('should set state when handleClick() is called', () => {
const component = shallow(<ArchiveExplorer itemCollection={data} />);

component.instance().handleClick({ name: 'subfolder' });
component.instance().handleClick({ fullPath: 'test/subfolder/' });

expect(component.state().fullPath).to.equal('test/subfolder/');
expect(component.state().view).to.equal(VIEWS.VIEW_FOLDER);
expect(component.state().searchQuery).to.equal('');
});
});

Expand All @@ -105,10 +108,11 @@ describe('lib/viewers/archive/ArchiveExplorer', () => {
const rowData = component.instance().getRowData(data)({ index: 0 });

const { KEY_NAME, KEY_MODIFIED_AT, KEY_SIZE } = TABLE_COLUMNS;
const { modified_at: modifiedAt, name, size, type, ...rest } = data[0];
const { absolute_path: fullPath, modified_at: modifiedAt, name, size, type, ...rest } = data[0];

expect(rowData).to.eql({
[KEY_NAME]: {
fullPath,
isExternal: false,
name,
type,
Expand All @@ -129,4 +133,34 @@ describe('lib/viewers/archive/ArchiveExplorer', () => {
expect(itemList).to.eql([data[1], data[2]]);
});
});

describe('search()', () => {
it('should set correct state when search query is not empty', () => {
const component = shallow(<ArchiveExplorer itemCollection={data} />);

component.instance().search('test');
expect(component.state().searchQuery).to.equal('test');
expect(component.state().view).to.equal(VIEWS.VIEW_SEARCH);

component.instance().search('');
expect(component.state().searchQuery).to.equal('');
expect(component.state().view).to.equal(VIEWS.VIEW_FOLDER);

component.instance().search(' ');
expect(component.state().searchQuery).to.equal(' ');
expect(component.state().view).to.equal(VIEWS.VIEW_FOLDER);
});
});

describe('getSearchResult()', () => {
it('should return correct item list', () => {
const component = shallow(<ArchiveExplorer itemCollection={data} />);

const itemList = component.instance().getSearchResult(data, 'level-1');
const fuzzyList = component.instance().getSearchResult(data, 'leel1');

expect(itemList).to.eql([data[1], data[2]]);
expect(fuzzyList).to.eql([data[1], data[2]]);
});
});
});
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5662,6 +5662,11 @@ functions-have-names@^1.1.1:
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.0.tgz#83da7583e4ea0c9ac5ff530f73394b033e0bf77d"
integrity sha512-zKXyzksTeaCSw5wIX79iCA40YAa6CJMJgNg9wdkU/ERBrIdPSimPICYiLp65lRbSBqtiHql/HZfS2DyI/AH6tQ==

fuzzysearch@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/fuzzysearch/-/fuzzysearch-1.0.3.tgz#dffc80f6d6b04223f2226aa79dd194231096d008"
integrity sha1-3/yA9tawQiPyImqnndGUIxCW0Ag=

gather-stream@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/gather-stream/-/gather-stream-1.0.0.tgz#b33994af457a8115700d410f317733cbe7a0904b"
Expand Down

0 comments on commit 41cc0a7

Please sign in to comment.