Skip to content

Commit

Permalink
Adding search and replace to datadocs (#30)
Browse files Browse the repository at this point in the history
* Adding search and replace to datadocs

* Generalize search and replace into a provider component, add feature to adhoc query
  • Loading branch information
czgu authored May 1, 2020
1 parent c5ef944 commit 75ecdd7
Show file tree
Hide file tree
Showing 41 changed files with 1,858 additions and 144 deletions.
38 changes: 38 additions & 0 deletions datahub/webapp/__tests__/lib/data-doc/search.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { replaceStringIndices } from 'lib/data-doc/search';

const testString = 'the quick brown fox jumps over the lazy dog';

test('Replace empty case', () => {
expect(replaceStringIndices(testString, [], 'ze')).toBe(
'the quick brown fox jumps over the lazy dog'
);
});

test('Replace simple case', () => {
expect(replaceStringIndices(testString, [[0, 3]], 'ze')).toBe(
'ze quick brown fox jumps over the lazy dog'
);
});

test('Replace multiple case', () => {
expect(
replaceStringIndices(
testString,
[
[0, 3],
[31, 34],
],
'le'
)
).toBe('le quick brown fox jumps over le lazy dog');
expect(
replaceStringIndices(
testString,
[
[16, 19],
[40, 43],
],
'tiger'
)
).toBe('the quick brown tiger jumps over the lazy tiger');
});
4 changes: 2 additions & 2 deletions datahub/webapp/__tests__/lib/utils/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as utils from 'lib/utils/index';

// missing getSelectionRect, download, copy, scrollToElement, smoothScroll
// missing getSelectionRect, download, copy, smoothScroll

test('removeEmpty', () => {
expect(utils.removeEmpty({ notempty: 'test', empty: null })).toStrictEqual({
Expand All @@ -18,7 +18,7 @@ test('titleize', () => {

test('sleep', () => {
const mockFunction = jest.fn(() => {
console.log('mock function runs');
// console.log('mock function runs');
});

const testFunction = async () => {
Expand Down
6 changes: 6 additions & 0 deletions datahub/webapp/components/DataDoc/DataDoc.scss
Original file line number Diff line number Diff line change
Expand Up @@ -207,4 +207,10 @@
}
}
}

.SearchAndReplaceBar {
position: fixed;
right: 60px;
z-index: 10;
}
}
177 changes: 132 additions & 45 deletions datahub/webapp/components/DataDoc/DataDoc.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import React from 'react';
import { connect } from 'react-redux';
import { ContentState } from 'draft-js';
import { findIndex, sample } from 'lodash';
import { findIndex } from 'lodash';
import { bind, debounce } from 'lodash-decorators';

import { decorate } from 'core-decorators';
import memoizeOne from 'memoize-one';
import classNames from 'classnames';

import { CELL_TYPE, IDataDoc, IDataCell } from 'const/datadoc';
import {
CELL_TYPE,
IDataDoc,
IDataCell,
DataCellUpdateFields,
} from 'const/datadoc';
import ds from 'lib/datasource';
import history from 'lib/router-history';
import { sendConfirm, sendNotification } from 'lib/dataHubUI';
Expand All @@ -34,7 +39,6 @@ import { DataDocRightSidebar } from 'components/DataDocRightSidebar/DataDocRight
import { DataDocUIGuide } from 'components/UIGuide/DataDocUIGuide';
import { DataDocCell } from 'components/DataDocCell/DataDocCell';

import { Title } from 'ui/Title/Title';
import { Message } from 'ui/Message/Message';
import { Loading } from 'ui/Loading/Loading';

Expand All @@ -44,8 +48,14 @@ import { DataDocError } from './DataDocError';
import { DataDocContentContainer } from './DataDocContentContainer';

import './DataDoc.scss';
import { DataDocLoading } from './DataDocLoading';

const loadingHints: string[] = require('config/loading_hints.yaml').hints;
import { searchDataDoc, replaceDataDoc } from 'lib/data-doc/search';
import {
ISearchAndReplaceHandles,
SearchAndReplace,
} from 'components/SearchAndReplace/SearchAndReplace';
import { ISearchOptions, ISearchResult } from 'const/searchAndReplace';

interface IOwnProps {
docId: number;
Expand All @@ -63,6 +73,8 @@ interface IState {
defaultCollapseAllCells: boolean;
cellIdToExecutionId: Record<number, number>;
highlightCellIndex?: number;

showSearchAndReplace: boolean;
}

class DataDocComponent extends React.Component<IProps, IState> {
Expand All @@ -74,8 +86,13 @@ class DataDocComponent extends React.Component<IProps, IState> {
connected: false,
defaultCollapseAllCells: null,
cellIdToExecutionId: {},

showSearchAndReplace: false,
};

//
public searchAndReplaceRef = React.createRef<ISearchAndReplaceHandles>();

public componentDidMount() {
this.autoFocusCell({}, this.props);
this.openDataDoc(this.props.docId);
Expand All @@ -95,14 +112,38 @@ class DataDocComponent extends React.Component<IProps, IState> {
this.setState({
defaultCollapseAllCells: null,
focusedCellIndex: null,

// Sharing State
cellIdToExecutionId: {},
highlightCellIndex: null,
});
// Reset search
this.searchAndReplaceRef.current?.reset();
}
this.openDataDoc(this.props.docId);
}

if (this.props.dataDoc !== prevProps.dataDoc && this.props.dataDoc) {
if (
this.props.dataDoc?.dataDocCells !== prevProps.dataDoc?.dataDocCells
) {
const cells = this.props.dataDoc?.dataDocCells ?? [];
const previousCells = prevProps.dataDoc?.dataDocCells ?? [];
const someCellsContextChanged =
cells.length !== previousCells.length ||
cells.some(
(cell, index) =>
cell.context !== previousCells[index].context
);

if (someCellsContextChanged) {
this.searchAndReplaceRef.current?.performSearch();
}
}

if (
this.props.dataDoc?.title !== prevProps.dataDoc?.title &&
this.props.dataDoc?.title
) {
this.publishDataDocTitle(this.props.dataDoc.title);
}
}
Expand Down Expand Up @@ -209,19 +250,21 @@ class DataDocComponent extends React.Component<IProps, IState> {
// This is to quickly snap to the element, and then in case
// if the cell above/below pushes the element out of view we
// try to scroll it back
scrollToCell(dataDoc.dataDocCells[cellIndex], 0).then(
() =>
this.setState(
{
highlightCellIndex: cellIndex,
},
() =>
scrollToCell(
dataDoc.dataDocCells[cellIndex],
200,
5
)
)
scrollToCell(
dataDoc.dataDocCells[cellIndex].id,
0
).then(() =>
this.setState(
{
highlightCellIndex: cellIndex,
},
() =>
scrollToCell(
dataDoc.dataDocCells[cellIndex].id,
200,
5
)
)
);
}
}
Expand All @@ -244,6 +287,35 @@ class DataDocComponent extends React.Component<IProps, IState> {
}
}

@bind
public getSearchResults(
searchString: string,
searchOptions: ISearchOptions
) {
return searchDataDoc(this.props.dataDoc, searchString, searchOptions);
}

@bind
public replace(
searchResultsToReplace: ISearchResult[],
replaceString: string
) {
return replaceDataDoc(
this.props.dataDoc,
searchResultsToReplace,
replaceString,
(cellId, context) => this.updateCell(cellId, { context })
);
}

@bind
public async jumpToResult(result: ISearchResult) {
const cellId = result?.cellId;
if (cellId != null) {
await scrollToCell(cellId, 0);
}
}

@bind
public async insertCellAt(
index: number,
Expand All @@ -268,6 +340,11 @@ class DataDocComponent extends React.Component<IProps, IState> {
}
}

@bind
public updateCell(cellId: number, fields: DataCellUpdateFields) {
return this.props.updateDataDocCell(this.props.docId, cellId, fields);
}

@bind
public handleToggleCollapse() {
this.setState(({ defaultCollapseAllCells }) => ({
Expand Down Expand Up @@ -320,6 +397,7 @@ class DataDocComponent extends React.Component<IProps, IState> {
},

insertCellAt: this.insertCellAt,
updateCell: this.updateCell,

defaultCollapse,
focusedCellIndex,
Expand Down Expand Up @@ -388,28 +466,6 @@ class DataDocComponent extends React.Component<IProps, IState> {
ds.save(`/datadoc/${this.props.docId}/run/`);
}

public makeDataDocLoadingDOM() {
// Get a random hint from list of hints
const hint = sample(loadingHints);

return (
<div className="datadoc-loading">
<div className="datadoc-loading-message">
<Title>
<i className="fa fa-spinner fa-pulse" />
&nbsp; Loading DataDoc
</Title>

<br />
<p className="subtitle">
<i className="far fa-lightbulb" />
&nbsp; Did you know: {hint}
</p>
</div>
</div>
);
}

@bind
public renderLazyDataDocCell(
cell: IDataCell,
Expand Down Expand Up @@ -499,7 +555,12 @@ class DataDocComponent extends React.Component<IProps, IState> {
changeDataDocMeta,
} = this.props;

const { connected, defaultCollapseAllCells } = this.state;
const {
connected,
defaultCollapseAllCells,

showSearchAndReplace,
} = this.state;

let docDOM = null;
let isSavingDataDoc = false;
Expand Down Expand Up @@ -540,7 +601,7 @@ class DataDocComponent extends React.Component<IProps, IState> {
</DataDocContentContainer>
);
} else {
docDOM = this.makeDataDocLoadingDOM();
docDOM = <DataDocLoading />;
}

const leftSideBar = (
Expand Down Expand Up @@ -571,9 +632,16 @@ class DataDocComponent extends React.Component<IProps, IState> {
key="data-hub-data-doc"
>
<DataDocContext.Provider value={this.getDataDocContextState()}>
{leftSideBar}
{docDOM}
{rightSideBar}
<SearchAndReplace
getSearchResults={this.getSearchResults}
jumpToResult={this.jumpToResult}
replace={this.replace}
ref={this.searchAndReplaceRef}
>
{leftSideBar}
{docDOM}
{rightSideBar}
</SearchAndReplace>
</DataDocContext.Provider>
</div>
);
Expand Down Expand Up @@ -659,6 +727,25 @@ function mapDispatchToProps(dispatch: Dispatch, ownProps: IOwnProps) {
meta
)
),

updateDataDocCell: (
docId: number,
cellId: number,
fields: DataCellUpdateFields
) => {
try {
return dispatch(
dataDocActions.updateDataDocCell(
docId,
cellId,
fields.context,
fields.meta
)
);
} catch (e) {
sendNotification(`Cannot update cell, reason: ${e}`);
}
},
};
}

Expand Down
26 changes: 26 additions & 0 deletions datahub/webapp/components/DataDoc/DataDocLoading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';
import { sample } from 'lodash';
import { Title } from 'ui/Title/Title';

const loadingHints: string[] = require('config/loading_hints.yaml').hints;

export const DataDocLoading: React.FC = () => {
const hint = sample(loadingHints);

return (
<div className="datadoc-loading">
<div className="datadoc-loading-message">
<Title>
<i className="fa fa-spinner fa-pulse" />
&nbsp; Loading DataDoc
</Title>

<br />
<p className="subtitle">
<i className="far fa-lightbulb" />
&nbsp; Did you know: {hint}
</p>
</div>
</div>
);
};
Loading

0 comments on commit 75ecdd7

Please sign in to comment.