Unverified Commit 1451a7ec authored by Allison Suarez Miranda's avatar Allison Suarez Miranda Committed by GitHub

feat: Make 'schema' and 'database' clickable to initiate a search (#766)

* search works, no checkbox marked
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* search for schema is working
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* added these || to set default values for missing props
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* changed the tests to kind of adjust to new testing practices, left some very much the same
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* fixed tests, all that's missing is the checkbox
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* fixed small == vs === typo
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* oops
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* prettier lint'
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* lintttt
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* filter now shows checked box
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* fixed prettier error
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>
parent 13164bf4
...@@ -2,15 +2,18 @@ ...@@ -2,15 +2,18 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
import * as React from 'react'; import * as React from 'react';
import { mocked } from 'ts-jest/utils'; import { mocked } from 'ts-jest/utils';
import { shallow } from 'enzyme'; import { mount } from 'enzyme';
import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store';
import { BrowserRouter } from 'react-router-dom';
import { ResourceType } from 'interfaces/Resources'; import { ResourceType } from 'interfaces/Resources';
import { import {
getSourceDisplayName, getSourceDisplayName,
getDisplayNameByResource, getDisplayNameByResource,
} from 'config/config-utils'; } from 'config/config-utils';
import globalState from 'fixtures/globalState';
import TableHeaderBullets, { TableHeaderBulletsProps } from '.'; import TableHeaderBullets, { TableHeaderBulletsProps } from '.';
const MOCK_RESOURCE_DISPLAY_NAME = 'Test'; const MOCK_RESOURCE_DISPLAY_NAME = 'Test';
...@@ -22,22 +25,62 @@ jest.mock('config/config-utils', () => ({ ...@@ -22,22 +25,62 @@ jest.mock('config/config-utils', () => ({
getSourceDisplayName: jest.fn(), getSourceDisplayName: jest.fn(),
})); }));
describe('TableHeaderBullets', () => { let noDatabase;
const setup = (propOverrides?: Partial<TableHeaderBulletsProps>) => { let noCluster;
const props: TableHeaderBulletsProps = { let noIsView;
database: 'hive',
cluster: 'main', const middlewares = [];
isView: true, const mockStore = configureStore(middlewares);
...propOverrides,
}; const setup = (propOverrides?: Partial<TableHeaderBulletsProps>) => {
const wrapper = shallow(<TableHeaderBullets {...props} />); const props = {
return { props, wrapper }; database: 'hive',
cluster: 'main',
isView: true,
...propOverrides,
}; };
const testState = globalState;
const wrapper = mount<TableHeaderBulletsProps>(
<Provider store={mockStore(testState)}>
<BrowserRouter>
<TableHeaderBullets {...props} />
</BrowserRouter>
</Provider>
);
return { props, wrapper };
};
describe('TableHeaderBullets', () => {
describe('when no props passed', () => {
const { wrapper } = setup({
database: noDatabase,
cluster: noCluster,
isView: noIsView,
});
it('renders TableHeaderBullets element', () => {
const actual = wrapper.find('.header-bullets').length;
const expected = 1;
expect(actual).toEqual(expected);
});
it('renders TableHeaderBullets list with default props', () => {
const actualDatabase = wrapper.find('ul').find('li').at(0).text();
const expectedDatabase = '';
const actualCluster = wrapper.find('ul').find('li').at(1).text();
const expectedCluster = '';
const actualIsView = wrapper.find('ul').find('li').at(2).text();
const expectedIsView = '';
expect(actualDatabase).toEqual(expectedDatabase);
expect(actualCluster).toEqual(expectedCluster);
expect(actualIsView).toEqual(expectedIsView);
});
});
describe('render', () => { describe('when props are defined', () => {
let props: TableHeaderBulletsProps; let props;
let wrapper; let wrapper;
let listElement;
beforeAll(() => { beforeAll(() => {
mocked(getSourceDisplayName).mockImplementation( mocked(getSourceDisplayName).mockImplementation(
() => MOCK_DB_DISPLAY_NAME () => MOCK_DB_DISPLAY_NAME
...@@ -48,34 +91,37 @@ describe('TableHeaderBullets', () => { ...@@ -48,34 +91,37 @@ describe('TableHeaderBullets', () => {
const setupResult = setup(); const setupResult = setup();
props = setupResult.props; props = setupResult.props;
wrapper = setupResult.wrapper; wrapper = setupResult.wrapper;
listElement = wrapper.find('ul');
}); });
it('renders a list with correct class', () => { it('renders TableHeaderBullets element', () => {
expect(listElement.props().className).toEqual('header-bullets'); const actual = wrapper.find('.header-bullets').length;
}); const expected = 1;
expect(actual).toEqual(expected);
});
it('renders a list with resource display name', () => { it('renders a list with resource display name', () => {
console.log(wrapper.debug());
expect(getDisplayNameByResource).toHaveBeenCalledWith(ResourceType.table); expect(getDisplayNameByResource).toHaveBeenCalledWith(ResourceType.table);
expect(listElement.find('li').at(0).text()).toEqual( expect(wrapper.find('ul').find('li').at(0).text()).toEqual(
MOCK_RESOURCE_DISPLAY_NAME MOCK_RESOURCE_DISPLAY_NAME
); );
}); });
it('renders a list with database display name', () => { it('renders a list with database display name', () => {
expect(getSourceDisplayName).toHaveBeenCalledWith( expect(getSourceDisplayName).toHaveBeenCalledWith(
props.database, props.database,
ResourceType.table ResourceType.table
); );
expect(listElement.find('li').at(1).text()).toEqual(MOCK_DB_DISPLAY_NAME); expect(wrapper.find('ul').find('li').at(1).text()).toEqual(
MOCK_DB_DISPLAY_NAME
);
}); });
it('renders a list with cluster', () => { it('renders a list with cluster', () => {
expect(listElement.find('li').at(2).text()).toEqual(props.cluster); expect(wrapper.find('ul').find('li').at(2).text()).toEqual(props.cluster);
}); });
it('renders a list with table view', () => { it('renders a list with table view', () => {
expect(listElement.find('li').at(3).text()).toEqual(TABLE_VIEW_TEXT); expect(wrapper.find('ul').find('li').at(3).text()).toEqual(
TABLE_VIEW_TEXT
);
}); });
}); });
}); });
// Copyright Contributors to the Amundsen project. // Copyright Contributors to the Amundsen project. /*
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
import * as React from 'react'; import * as React from 'react';
import { Link } from 'react-router-dom';
import { import {
getDisplayNameByResource, getDisplayNameByResource,
getSourceDisplayName, getSourceDisplayName,
} from 'config/config-utils'; } from 'config/config-utils';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { updateSearchState } from 'ducks/search/reducer';
import { UpdateSearchStateRequest } from 'ducks/search/types';
import { logClick } from 'ducks/utilMethods';
import { ResourceType } from 'interfaces/Resources'; import { ResourceType } from 'interfaces/Resources';
import { TABLE_VIEW_TEXT } from './constants'; import { TABLE_VIEW_TEXT } from './constants';
export interface TableHeaderBulletsProps { export interface HeaderBulletsProps {
cluster: string; cluster: string;
database: string; database: string;
isView: boolean; isView: boolean;
} }
export interface DispatchFromProps {
searchDatabase: (databaseText: string) => UpdateSearchStateRequest;
}
const TableHeaderBullets: React.FC<TableHeaderBulletsProps> = ({ export type TableHeaderBulletsProps = HeaderBulletsProps & DispatchFromProps;
cluster,
database,
isView,
}: TableHeaderBulletsProps) => {
return (
<ul className="header-bullets">
<li>{getDisplayNameByResource(ResourceType.table)}</li>
<li>{getSourceDisplayName(database, ResourceType.table)}</li>
<li>{cluster}</li>
{isView && <li>{TABLE_VIEW_TEXT}</li>}
</ul>
);
};
TableHeaderBullets.defaultProps = { export class TableHeaderBullets extends React.Component<
cluster: '', TableHeaderBulletsProps
database: '', > {
isView: false, handleClick = (e) => {
const databaseText = this.props.database;
logClick(e, {
target_type: 'database',
label: databaseText,
});
this.props.searchDatabase(databaseText);
};
render() {
const isViewCheck =
this.props.isView === undefined ? false : this.props.isView;
return (
<ul className="header-bullets">
<li>{getDisplayNameByResource(ResourceType.table)}</li>
<li>
<Link to="/search" onClick={this.handleClick}>
{getSourceDisplayName(
this.props.database || '',
ResourceType.table
)}
</Link>
</li>
<li>{this.props.cluster || ''}</li>
{isViewCheck && <li>{TABLE_VIEW_TEXT}</li>}
</ul>
);
}
}
export const mapDispatchToProps = (dispatch: any) => {
return bindActionCreators(
{
searchDatabase: (databaseText: string) =>
updateSearchState({
filters: {
[ResourceType.table]: { database: { [databaseText]: true } },
},
submitSearch: true,
}),
},
dispatch
);
}; };
export default TableHeaderBullets; export default connect<null, DispatchFromProps, HeaderBulletsProps>(
null,
mapDispatchToProps
)(TableHeaderBullets);
...@@ -41,6 +41,7 @@ const setup = ( ...@@ -41,6 +41,7 @@ const setup = (
tableData: tableMetadata, tableData: tableMetadata,
getTableData: jest.fn(), getTableData: jest.fn(),
openRequestDescriptionDialog: jest.fn(), openRequestDescriptionDialog: jest.fn(),
searchSchema: jest.fn(),
...routerProps, ...routerProps,
...propOverrides, ...propOverrides,
}; };
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
import * as React from 'react'; import * as React from 'react';
import { Link } from 'react-router-dom';
import * as DocumentTitle from 'react-document-title'; import * as DocumentTitle from 'react-document-title';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
...@@ -10,8 +11,11 @@ import { RouteComponentProps } from 'react-router'; ...@@ -10,8 +11,11 @@ import { RouteComponentProps } from 'react-router';
import { GlobalState } from 'ducks/rootReducer'; import { GlobalState } from 'ducks/rootReducer';
import { getTableData } from 'ducks/tableMetadata/reducer'; import { getTableData } from 'ducks/tableMetadata/reducer';
import { openRequestDescriptionDialog } from 'ducks/notification/reducer'; import { openRequestDescriptionDialog } from 'ducks/notification/reducer';
import { updateSearchState } from 'ducks/search/reducer';
import { GetTableDataRequest } from 'ducks/tableMetadata/types'; import { GetTableDataRequest } from 'ducks/tableMetadata/types';
import { OpenRequestAction } from 'ducks/notification/types'; import { OpenRequestAction } from 'ducks/notification/types';
import { UpdateSearchStateRequest } from 'ducks/search/types';
import { logClick } from 'ducks/utilMethods';
import { import {
getDescriptionSourceDisplayName, getDescriptionSourceDisplayName,
...@@ -90,6 +94,7 @@ export interface DispatchFromProps { ...@@ -90,6 +94,7 @@ export interface DispatchFromProps {
requestMetadataType: RequestMetadataType, requestMetadataType: RequestMetadataType,
columnName: string columnName: string
) => OpenRequestAction; ) => OpenRequestAction;
searchSchema: (schemaText: string) => UpdateSearchStateRequest;
} }
export interface MatchProps { export interface MatchProps {
...@@ -170,6 +175,17 @@ export class TableDetail extends React.Component< ...@@ -170,6 +175,17 @@ export class TableDetail extends React.Component<
return `${params.database}://${params.cluster}.${params.schema}/${params.table}`; return `${params.database}://${params.cluster}.${params.schema}/${params.table}`;
} }
handleClick = (e) => {
const { match } = this.props;
const { params } = match;
const schemaText = params.schema;
logClick(e, {
target_type: 'schema',
label: schemaText,
});
this.props.searchSchema(schemaText);
};
renderProgrammaticDesc = ( renderProgrammaticDesc = (
descriptions: ProgrammaticDescription[] | undefined descriptions: ProgrammaticDescription[] | undefined
) => { ) => {
...@@ -290,7 +306,10 @@ export class TableDetail extends React.Component< ...@@ -290,7 +306,10 @@ export class TableDetail extends React.Component<
</div> </div>
<div className="header-section header-title"> <div className="header-section header-title">
<h1 className="header-title-text truncated"> <h1 className="header-title-text truncated">
{this.getDisplayName()} <Link to="/search" onClick={this.handleClick}>
{data.schema}
</Link>
.{data.name}
</h1> </h1>
<BookmarkIcon <BookmarkIcon
bookmarkKey={data.key} bookmarkKey={data.key}
...@@ -432,7 +451,17 @@ export const mapStateToProps = (state: GlobalState) => { ...@@ -432,7 +451,17 @@ export const mapStateToProps = (state: GlobalState) => {
export const mapDispatchToProps = (dispatch: any) => { export const mapDispatchToProps = (dispatch: any) => {
return bindActionCreators( return bindActionCreators(
{ getTableData, openRequestDescriptionDialog }, {
getTableData,
openRequestDescriptionDialog,
searchSchema: (schemaText: string) =>
updateSearchState({
filters: {
[ResourceType.table]: { schema: schemaText },
},
submitSearch: true,
}),
},
dispatch dispatch
); );
}; };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment