Skip to content

Commit

Permalink
Workplace Search in Kibana MVP (#70979)
Browse files Browse the repository at this point in the history
* Add Workplace Search plugin to app

- Adds telemetry for Workplace Search
- Adds routing for telemetry and overview
- Registers plugin

* Add breadcrumbs for Workplace Search

* Add Workplace Search index

* Add route paths, types and shared assets

* Add shared Workplace Search components

* Add setup guide to Workplace Search

* Add error state to Workplace Search

* Add Workplace Search overview

This is the functional MVP for Workplace Search

* Update telemetry per recent changes

- Remove saved objects indexing
- add schema definition
- remove no_ws_account
- minor cleanup

* Fix pluralization syntax

- Still not working but fixed the syntax nonetheless

* Change pluralization method

- Was unable to get the `FormattedMessage` to work using the syntax in the docs. Always added ‘more’, even when there were zero (or one for users). This commit uses an alternative approach that works

* Update readme

* Fix duplicate i18n label

* Fix failing test from previous commit

:facepalm:

* Update link for image in Setup Guide

* Remove need for hash in routes

Because of a change in the Workplace Search rails code, we can now use non-hash routes that will be redirected by rails so that we don’t have users stuck on the overview page in Workplace Search when logging in

* Directly link to source details from activity feed

Previously the dashboard in legacy Workplace Search linked to the sources page and this was replicated in the Kibana MVP. This PR aligns with the legacy dashboard directly linking to the source details

https://github.com/elastic/ent-search/pull/1688

* Add warn logging to Workplace Search telemetry collector

* Change casing to camel to match App Search

* Misc security feedback for Workplace Search

* Update licence mocks to match App Search

* PR feedback from App Search PR

* REmove duplicate code from merge conflict

* Fix tests

* Move varible declaration inside map for TypeScript

There was no other way 🤦

* Refactor last commit

* Add punctuation

Smallest commit ever.

* Fix actionPath type errors

* Update rebase feedback

* Fix failing test

* Update telemetry test after AS PR feedback

* DRY out error state prompt copy

* DRY out telemetry endpoint into a single route + DRY out

DRY out endpoint
- Instead of /api/app_search/telemetry & /api/workplace_search/telemetry, just have a single /api/enterprise_search/telemetry endpoint that takes a product param
- Update public/send_telemetry accordingly (+ write tests for SendWorkplaceSearchTelemetry)

DRY out helpers
- Pull out certain reusable helper functions into a shared lib/ folder and have them take the repo id/name as a param
- Move tests over
- Remove misplaced comment block

+BONUS
- pull out content type header that's been giving us grief in Chrome into a constant

* Remove unused telemetry type

* Minor server cleanup - DRY out mockLogger

* Setup Guide cleanup

* Clean up Loading component

- use EUI vars per feedback
- remove unnecessary wrapper
- adjust vh for Kibana layout
- Actually apply loadingSpinner styles

* Misc i18n fixes

+ minor newline reduction, because prettier lets me

* Refactor Recent Activity component/styles

- Remove table markup/styles - not semantically correct or accessible in this case - replace w flex
- Fix link colors not inheriting
- Add EuiPanel, error colors looked odd against page background
- Fix prop/type definition
- CSS cleanup - EUI vars, correct BEM, don't target generic selectors

* [Opinionated] Refactor RecentActivity component

- Pull out iterated activity items into a child subcomponent
- Move constants/strings closer to where they're being used, instead of having to jump around the file
- Move IActivityFeed definition to this file, since that's primarily where it's used

@scottybollinger - if you're not a fan of this commit no worries, just let me know and we can discuss/roll back as needed

* Refactor ViewContentHeader

- remove unused CSS
- fallback cleanup
- refactor tests

* Refactor ContentSection

- Remove unused CSS classes
- Refactor tests to include all props/more specific assertions

* Refactor StatisticCard

- Prefer using EuiTextColor to spans / custom classes
- Prefer using EuiCard's native `href` behavior over using our own wrapping link/--isClickablec class
- Note that when we port the link/destination over to React Router, we should instead opt to use React Router history, which will involve creating a EuiCard helper
- Make test a bit more specific

* Minor OrganizationStats cleanup

- Use EuiFlexGrid

* Refactor OnboardingSteps

- i18n
    - Compact i18n newlines (nit)
    - Convert FormattedMessage to i18n.translate for easier test assertions
- Org Name CTA
    - Move to separate child subcomponent to make it easier to quickly skim the parent container
    - Remove unused CSS class
    - Fix/add responsive behavior

- Tests refactor
    - Use describe() blocks to break up tests by card/section
    - Make sure each card has tests for each state - zero, some/complete, and disabled/no access
    - Assert by plain text now that we're using i18n.translate()
    - Remove ContentSection/EuiPanel assertions - they're not terribly useful, and we have more specific elements to check
    - Add accounts={0} test to satisfy yellow branch line

* Clean up OnboardingCard

- Remove unused CSS class
- Remove unnecessary template literal

Tests
- Swap out check for EuiFlexItem - it's not really the content we're concerned about displaying, EuiEmptyPrompt is the primary component
- Remove need for mount() by dive()ing into EuiEmptyPrompt (this also removes the need to specify a[data-test-subj] instead of just [data-test-subj])
- Simplify empty button test - previous test has already checked for href/telemetry
- Cover uncovered actionPath branch line

* Minor Overview cleanup

- Remove unused telemetry type
- Remove unused CSS class
- finally
- Remove unused license context from tests

* Feedback: UI fixes

- Fix setup guide CSS class casing
- Remove border transparent (UX > UI)

* Fix Workplace Search not being hidden on feature control

- Whoops, totally missed this 🤦

* Add very basic functional Workplace Search test

- Has to be without_host_configured, since with host requires Enterprise Search
- Just checks for basic Setup Guide redirect for now
- TODO: Add more in-depth feature/privilege functional tests for both plugins at later date

* Pay down test render/loading tech debt

- Turns out you don't need render(), shallow() skips useEffect already 🤦
- Fix outdated comment import example

* DRY out repeated mountWithApiMock into mountWithAsyncContext

+ Minor engines_overview test refactors:
    - Prefer to define `const wrapper` at the start of each test rather than a `let wrapper` - this better for sandboxing / not leaking state between tests
    - Move Platinum license tests above pagination, so the contrast between the two tests are easier to grok

* Design feedback

- README copy tweak + linting
- Remove unused euiCard classes from onboarding card

Co-authored-by: Constance Chen <constance.chen.3@gmail.com>
  • Loading branch information
scottybollinger and cee-chen committed Jul 13, 2020
1 parent 7029bb7 commit e660328
Show file tree
Hide file tree
Showing 85 changed files with 2,908 additions and 321 deletions.
5 changes: 4 additions & 1 deletion x-pack/plugins/enterprise_search/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

## Overview

This plugin's goal is to provide a Kibana user interface to the Enterprise Search solution's products (App Search and Workplace Search). In its current MVP state, the plugin provides a basic engines overview from App Search with the goal of gathering user feedback and raising product awareness.
This plugin's goal is to provide a Kibana user interface to the Enterprise Search solution's products (App Search and Workplace Search). In it's current MVP state, the plugin provides the following with the goal of gathering user feedback and raising product awareness:

- **App Search:** A basic engines overview with links into the product.
- **Workplace Search:** A simple app overview with basic statistics, links to the sources, users (if standard auth), and product settings.

## Development

Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/enterprise_search/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/

export const JSON_HEADER = { 'Content-Type': 'application/json' }; // This needs specific casing or Chrome throws a 415 error

export const ENGINES_PAGE_SIZE = 10;
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
export { mockHistory } from './react_router_history.mock';
export { mockKibanaContext } from './kibana_context.mock';
export { mockLicenseContext } from './license_context.mock';
export { mountWithContext, mountWithKibanaContext } from './mount_with_context.mock';
export {
mountWithContext,
mountWithKibanaContext,
mountWithAsyncContext,
} from './mount_with_context.mock';
export { shallowWithIntl } from './shallow_with_i18n.mock';

// Note: shallow_usecontext must be imported directly as a file
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
*/

import React from 'react';
import { mount } from 'enzyme';
import { act } from 'react-dom/test-utils';
import { mount, ReactWrapper } from 'enzyme';

import { I18nProvider } from '@kbn/i18n/react';
import { KibanaContext } from '../';
Expand Down Expand Up @@ -47,3 +48,33 @@ export const mountWithKibanaContext = (children: React.ReactNode, context?: obje
</KibanaContext.Provider>
);
};

/**
* This helper is intended for components that have async effects
* (e.g. http fetches) on mount. It mostly adds act/update boilerplate
* that's needed for the wrapper to play nice with Enzyme/Jest
*
* Example usage:
*
* const wrapper = mountWithAsyncContext(<Component />, { http: { get: () => someData } });
*/
export const mountWithAsyncContext = async (
children: React.ReactNode,
context: object
): Promise<ReactWrapper> => {
let wrapper: ReactWrapper | undefined;

// We get a lot of act() warning/errors in the terminal without this.
// TBH, I don't fully understand why since Enzyme's mount is supposed to
// have act() baked in - could be because of the wrapping context provider?
await act(async () => {
wrapper = mountWithContext(children, context);
});
if (wrapper) {
wrapper.update(); // This seems to be required for the DOM to actually update

return wrapper;
} else {
throw new Error('Could not mount wrapper');
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jest.mock('react', () => ({
/**
* Example usage within a component test using shallow():
*
* import '../../../test_utils/mock_shallow_usecontext'; // Must come before React's import, adjust relative path as needed
* import '../../../__mocks__/shallow_usecontext'; // Must come before React's import, adjust relative path as needed
*
* import React from 'react';
* import { shallow } from 'enzyme';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import '../../../__mocks__/shallow_usecontext.mock';
import React from 'react';
import { shallow } from 'enzyme';
import { EuiEmptyPrompt, EuiButton, EuiLoadingContent } from '@elastic/eui';
import { ErrorStatePrompt } from '../../../shared/error_state';

jest.mock('../../../shared/telemetry', () => ({
sendTelemetry: jest.fn(),
Expand All @@ -22,7 +23,7 @@ describe('ErrorState', () => {
it('renders', () => {
const wrapper = shallow(<ErrorState />);

expect(wrapper.find(EuiEmptyPrompt)).toHaveLength(1);
expect(wrapper.find(ErrorStatePrompt)).toHaveLength(1);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,90 +4,26 @@
* you may not use this file except in compliance with the Elastic License.
*/

import React, { useContext } from 'react';
import { EuiPage, EuiPageBody, EuiPageContent, EuiEmptyPrompt, EuiCode } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import React from 'react';
import { EuiPage, EuiPageBody, EuiPageContent } from '@elastic/eui';

import { EuiButton } from '../../../shared/react_router_helpers';
import { ErrorStatePrompt } from '../../../shared/error_state';
import { SetAppSearchBreadcrumbs as SetBreadcrumbs } from '../../../shared/kibana_breadcrumbs';
import { SendAppSearchTelemetry as SendTelemetry } from '../../../shared/telemetry';
import { KibanaContext, IKibanaContext } from '../../../index';
import { EngineOverviewHeader } from '../engine_overview_header';

import './empty_states.scss';

export const ErrorState: React.FC = () => {
const { enterpriseSearchUrl } = useContext(KibanaContext) as IKibanaContext;

return (
<EuiPage restrictWidth>
<SetBreadcrumbs isRoot />
<SendTelemetry action="error" metric="cannot_connect" />

<EuiPageBody>
<EngineOverviewHeader isButtonDisabled />
<EuiPageContent className="emptyState">
<EuiEmptyPrompt
className="emptyState__prompt"
iconType="alert"
iconColor="danger"
title={
<h2>
<FormattedMessage
id="xpack.enterpriseSearch.appSearch.errorConnectingState.title"
defaultMessage="Unable to connect"
/>
</h2>
}
titleSize="l"
body={
<>
<p>
<FormattedMessage
id="xpack.enterpriseSearch.appSearch.errorConnectingState.description1"
defaultMessage="We can’t establish a connection to App Search at the host URL: {enterpriseSearchUrl}"
values={{
enterpriseSearchUrl: <EuiCode>{enterpriseSearchUrl}</EuiCode>,
}}
/>
</p>
<ol className="eui-textLeft">
<li>
<FormattedMessage
id="xpack.enterpriseSearch.appSearch.errorConnectingState.description2"
defaultMessage="Ensure the host URL is configured correctly in {configFile}."
values={{
configFile: <EuiCode>config/kibana.yml</EuiCode>,
}}
/>
</li>
<li>
<FormattedMessage
id="xpack.enterpriseSearch.appSearch.errorConnectingState.description3"
defaultMessage="Confirm that the App Search server is responsive."
/>
</li>
<li>
<FormattedMessage
id="xpack.enterpriseSearch.appSearch.errorConnectingState.description4"
defaultMessage="Review the Setup guide or check your server log for {pluginLog} log messages."
values={{
pluginLog: <EuiCode>[enterpriseSearch][plugins]</EuiCode>,
}}
/>
</li>
</ol>
</>
}
actions={
<EuiButton iconType="help" fill to="/setup_guide">
<FormattedMessage
id="xpack.enterpriseSearch.appSearch.errorConnectingState.setupGuideCta"
defaultMessage="Review setup guide"
/>
</EuiButton>
}
/>
<EuiPageContent>
<ErrorStatePrompt />
</EuiPageContent>
</EuiPageBody>
</EuiPage>
Expand Down
Loading

0 comments on commit e660328

Please sign in to comment.