Skip to content

Commit

Permalink
work-in-progress fulltext part of plugin, jsonnet building/vendoring …
Browse files Browse the repository at this point in the history
…updated
  • Loading branch information
Erbenos committed Jul 19, 2020
1 parent 3f79542 commit 3314726
Show file tree
Hide file tree
Showing 116 changed files with 7,698 additions and 60 deletions.
4 changes: 3 additions & 1 deletion .prettierrc.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
module.exports = {
...require("./node_modules/@grafana/toolkit/src/config/prettier.plugin.config.json"),
...require('./node_modules/@grafana/toolkit/src/config/prettier.plugin.config.json'),
// If this is not here, prettier takes default 'always', which doesnt seem to be the rule that grafana-toolkit is linting by
arrowParens: 'avoid',
};
12 changes: 8 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,24 @@
# build grafana-pcp
#
YARN = yarn
JSONNET_PATH = ../grafonnet-lib
DASHBOARD_DIR := src/dashboards
DASHBOARDS := $(addprefix $(DASHBOARD_DIR)/,pcp-vector-host-overview.json pcp-vector-bcc-overview.json)
JSONNET_DEPS := src/dashboard/jsonnetfile.json
DASHBOARDS := $(addprefix $(DASHBOARD_DIR)/,pcp-vector-host-overview.json pcp-vector-bcc-overview.json fulltext-graph-preview.json fulltext-table-preview.json)

default: build

node_modules: package.json
$(YARN) install
sed -i '[email protected](createIgnoreResult(filePath, cwd));@// &@' node_modules/eslint/lib/cli-engine/cli-engine.js

$(JSONNET_DEPS):
cd $(DASHBOARD_DIR) && jb install

$(DASHBOARD_DIR)/%.json: $(DASHBOARD_DIR)/%.jsonnet
jsonnet -J $(JSONNET_PATH) -o $@ $<
cd src/dashboards && jb install
jsonnet -o $@ $<

dist: node_modules $(DASHBOARDS)
dist: node_modules $(JSONNET_DEPS) $(DASHBOARDS)
$(YARN) run build

build: dist
Expand Down
5 changes: 5 additions & 0 deletions config/jest-setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Enzyme from 'enzyme';
import EnzymeAdapter from 'enzyme-adapter-react-16';

// Setup enzyme's react adapter
Enzyme.configure({ adapter: new EnzymeAdapter() });
22 changes: 21 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,37 @@
"@grafana/runtime": "^7.0.6",
"@grafana/toolkit": "^7.0.6",
"@grafana/ui": "^7.0.6",
"@types/enzyme": "^3.10.5",
"@types/enzyme-adapter-react-16": "^1.0.6",
"@types/grafana": "github:CorpGlory/types-grafana",
"@types/jest": "^26.0.5",
"@types/lodash": "^4.14.157",
"@types/react-autosuggest": "^9.3.14",
"@types/react-redux": "^7.1.9",
"@types/redux": "^3.6.0",
"@types/redux-persist": "^4.3.1",
"@types/redux-persist-transform-filter": "^0.0.4",
"@types/redux-thunk": "^2.1.0",
"babel-plugin-remove-object-properties": "^1.0.2",
"benchmark": "^2.1.4",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.2",
"jest-date-mock": "^1.0.8",
"prettier": "^2.0.5",
"worker-loader": "^2.0.0"
},
"engines": {
"node": ">=12 <13"
},
"dependencies": {
"core-js": "^3.6.5",
"d3-flame-graph": "^3.1.1"
"d3-flame-graph": "^3.1.1",
"emotion": "^10.0.27",
"lodash": "^4.17.19",
"react-autosuggest": "^10.0.2",
"react-redux": "^7.2.0",
"redux": "^4.0.5",
"redux-persist": "^6.0.0",
"redux-thunk": "^2.3.0"
}
}
46 changes: 46 additions & 0 deletions src/components/search/App.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { shallow } from 'enzyme';
import React from 'react';
import { App, AppProps } from './App';
import { ViewState } from './store/slices/search/slices/view/state';

describe('<App/>', () => {
let appProps: AppProps;

beforeEach(() => {
appProps = { view: ViewState.Index };
});

test('renders without crashing', () => {
shallow(<App {...appProps} />);
});

test('can render detail page', () => {
const wrapper = shallow(<App {...{ ...appProps, view: ViewState.Detail }} />);
expect(wrapper.exists('[data-test="detail-page"]')).toBe(true);
});

test('can render search page', () => {
const wrapper = shallow(<App {...{ ...appProps, view: ViewState.Search }} />);
expect(wrapper.exists('[data-test="search-page"]')).toBe(true);
});

test('can render index page', () => {
const wrapper = shallow(<App {...{ ...appProps, view: ViewState.Index }} />);
expect(wrapper.exists('[data-test="index-page"]')).toBe(true);
});

test('renders search form', () => {
const wrapper = shallow(<App {...appProps} />);
expect(wrapper.exists('[data-test="search-form"]')).toBe(true);
});

test('renders actions', () => {
const wrapper = shallow(<App {...appProps} />);
expect(wrapper.exists('[data-test="actions"]')).toBe(true);
});

test('renders aside', () => {
const wrapper = shallow(<App {...appProps} />);
expect(wrapper.exists('[data-test="aside"]')).toBe(true);
});
});
53 changes: 53 additions & 0 deletions src/components/search/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from 'react';
import { RootState } from './store/reducer';
import { ViewState } from './store/slices/search/slices/view/state';
import DetailPage from './pages/Detail/DetailPage';
import SearchPage from './pages/Search/SearchPage';
import IndexPage from './pages/Index/IndexPage';
import { appLayout } from './styles';
import SearchForm from './partials/SearchForm/SearchForm';
import Actions from './partials/Actions/Actions';
import Aside from './partials/Aside/Aside';
import { connect } from 'react-redux';

const mapStateToProps = (state: RootState) => ({
view: state.search.view,
});

export type AppReduxStateProps = ReturnType<typeof mapStateToProps>;

export type AppProps = AppReduxStateProps;

export class App extends React.Component<AppProps, {}> {
constructor(props: AppProps) {
super(props);
}

renderPageComponent() {
const { view } = this.props;

switch (view) {
case ViewState.Detail:
return <DetailPage data-test="detail-page" />;
case ViewState.Search:
return <SearchPage data-test="search-page" />;
case ViewState.Index:
return <IndexPage data-test="index-page" />;
default:
return;
}
}

render() {
return (
<div className={appLayout}>
<SearchForm data-test="search-form" />
<Actions data-test="actions" />
<Aside data-test="aside" />
{this.renderPageComponent()}
</div>
);
}
}

export default connect(mapStateToProps, {})(App);
99 changes: 99 additions & 0 deletions src/components/search/Search.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import React from 'react';
import { AppRootProps } from '@grafana/data';
import { Provider } from 'react-redux';
import App from './App';
import { PersistGate } from 'redux-persist/integration/react';
import { Persistor } from 'redux-persist';
import Loader from './components/Loader/Loader';
import { initStore } from './store/store';
import { Store, AnyAction } from 'redux';
import { initServices, Services } from './services/services';
import ServicesContext from './contexts/services';

interface AppRootState {
store: Store<any, AnyAction> | null;
services: Services | null;
persistor: Persistor | null;
loading: boolean;
errorMsg: string;
}

export class Search extends React.Component<AppRootProps, AppRootState> {
state: AppRootState = {
store: null,
services: null,
persistor: null,
loading: true,
errorMsg: '',
};

constructor(props: AppRootProps) {
super(props);
}

componentDidMount() {
// Bloat for Grafana App plugin tabs, which we don't actually use in app itself, hence this wrapper
this.updateNav();
initServices()
.then(services => {
this.setState({ services });
return initStore(services);
})
.then(({ store, persistor }) => {
this.setState({ store, persistor, loading: false });
})
.catch((err: Error) => {
this.setState({ loading: false, errorMsg: err.message });
});
}

componentDidUpdate(prevProps: AppRootProps) {
if (this.props.query !== prevProps.query) {
if (this.props.query.tab !== prevProps.query.tab) {
this.updateNav();
}
}
}

updateNav() {
const { path, onNavChanged, meta } = this.props;
const node = {
text: 'Performance Co-Pilot',
img: meta.info.logos.large,
subTitle: 'Full-Text Search',
url: path,
};
onNavChanged({
node,
main: node,
});
}

// Render main App component without above bloat
render() {
const { store, loading, persistor, errorMsg, services } = this.state;
if (loading) {
return <Loader loaded={false} />;
}
if (store === null || services === null) {
return <p>{errorMsg}</p>;
}
return (
<Provider store={store}>
{/* Seems like redux-persist has really buggy typings
// @ts-ignore */}
<PersistGate persistor={persistor}>
{loaded => (
<Loader loaded={loaded}>
<ServicesContext.Provider value={services}>
<App />
</ServicesContext.Provider>
</Loader>
)}
</PersistGate>
</Provider>
);
}
}

export default Search;
104 changes: 104 additions & 0 deletions src/components/search/components/BookmarkList/BookmarkList.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import React from 'react';
import { shallow } from 'enzyme';
import { BookmarkList } from './BookmarkList';
import { GrafanaThemeType } from '@grafana/data';
import { getTheme } from '@grafana/ui';
import { BookmarkItem } from '../../store/slices/search/slices/bookmarks/state';
import { EntityType } from '../../models/endpoints/search';

describe('<BookmarkList/>', () => {
const placeholderCallbacks = {
onBookmarkClick: () => void 0,
onClearBookmarksClick: () => void 0,
};
const theme = getTheme(GrafanaThemeType.Light);

const bookmarkItems: BookmarkItem[] = [
{
id: 'statsd.settings.dropped',
type: EntityType.Metric,
},
{
id: '60.2',
type: EntityType.InstanceDomain,
},
];

test('renders without crashing', () => {
shallow(<BookmarkList bookmarks={[]} {...placeholderCallbacks} theme={theme} />);
});

test('renders multiple columns by default', () => {
const component = shallow(<BookmarkList bookmarks={bookmarkItems} {...placeholderCallbacks} theme={theme} />);
expect(component.exists('[data-test="multicol"]')).toBe(true);
});

test('renders clear button by default', () => {
const component = shallow(<BookmarkList bookmarks={bookmarkItems} {...placeholderCallbacks} theme={theme} />);
expect(component.exists('[data-test="bookmark-reset"]')).toBe(true);
});

test('accepts both single column and multi column prop settings', () => {
const singleCol = shallow(
<BookmarkList bookmarks={bookmarkItems} multiCol={false} {...placeholderCallbacks} theme={theme} />
);
expect(singleCol.exists('[data-test="singlecol"]')).toBe(true);

const multiCol = shallow(
<BookmarkList bookmarks={bookmarkItems} multiCol={true} {...placeholderCallbacks} theme={theme} />
);
expect(multiCol.exists('[data-test="multicol"]')).toBe(true);
});

test('accepts conditional showing of clear button', () => {
const hasClear = shallow(
<BookmarkList bookmarks={bookmarkItems} showClearBtn={true} {...placeholderCallbacks} theme={theme} />
);
expect(hasClear.exists('[data-test="bookmark-reset"]')).toBe(true);

const lacksClear = shallow(
<BookmarkList bookmarks={bookmarkItems} showClearBtn={false} {...placeholderCallbacks} theme={theme} />
);
expect(lacksClear.exists('[data-test="bookmark-reset"]')).toBe(false);
});

test('calls onBookmarkClick properly', () => {
const onBookmarkClickMock = jest.fn(() => void 0);
const component = shallow(
<BookmarkList
bookmarks={bookmarkItems}
{...placeholderCallbacks}
onBookmarkClick={onBookmarkClickMock}
theme={theme}
/>
);
const buttons = component.find('[data-test="bookmark-go"]');
buttons.forEach(button => button.simulate('click'));
expect(onBookmarkClickMock).toHaveBeenCalledTimes(bookmarkItems.length);
});

test('calls onClearBookmarksClick properly', () => {
const onClearBookmarksClickMock = jest.fn(() => void 0);
const component = shallow(
<BookmarkList
bookmarks={bookmarkItems}
{...placeholderCallbacks}
onClearBookmarksClick={onClearBookmarksClickMock}
theme={theme}
/>
);
const button = component.find('[data-test="bookmark-reset"]');
button.simulate('click');
expect(onClearBookmarksClickMock).toHaveBeenCalled();
});

test('renders passed all bookmarks items', () => {
const component = shallow(<BookmarkList bookmarks={bookmarkItems} {...placeholderCallbacks} theme={theme} />);
expect(component.find('[data-test="bookmark-go"]').length).toBe(bookmarkItems.length);
});

test('handles no bookmarks items', () => {
const component = shallow(<BookmarkList bookmarks={[]} {...placeholderCallbacks} theme={theme} />);
expect(component.exists('[data-test="bookmark-go"]')).toBe(false);
});
});
Loading

0 comments on commit 3314726

Please sign in to comment.