* Poprt #3567 to Kibana https://github.com/elastic/ent-search/pull/3567 * Poer #3582 to Kibana https://github.com/elastic/ent-search/pull/3582 Also adds missing i18n * Port #3634 to Kibana https://github.com/elastic/ent-search/pull/3634 * Port #3758 to Kibana * Rename var Co-authored-by: Scotty Bollinger <scotty.bollinger@elastic.co>
This commit is contained in:
parent
97a5c09b56
commit
99badfe169
8 changed files with 124 additions and 33 deletions
|
@ -24,6 +24,7 @@ export const contentSources = [
|
|||
errorReason: null,
|
||||
allowsReauth: true,
|
||||
boost: 1,
|
||||
activities: [],
|
||||
},
|
||||
{
|
||||
id: '124',
|
||||
|
@ -38,6 +39,7 @@ export const contentSources = [
|
|||
errorReason: null,
|
||||
allowsReauth: true,
|
||||
boost: 0.5,
|
||||
activities: [],
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const SOURCE_ROW_REAUTHENTICATE_STATUS_LINK_LABEL = i18n.translate(
|
||||
'xpack.enterpriseSearch.workplaceSearch.sourceRow.reauthenticateStatusLinkLabel',
|
||||
{
|
||||
defaultMessage: 'Re-authenticate',
|
||||
}
|
||||
);
|
||||
|
||||
export const SOURCE_ROW_REMOTE_LABEL = i18n.translate(
|
||||
'xpack.enterpriseSearch.workplaceSearch.sourceRow.remoteLabel',
|
||||
{
|
||||
defaultMessage: 'Remote',
|
||||
}
|
||||
);
|
||||
|
||||
export const SOURCE_ROW_REMOTE_TOOLTIP = i18n.translate(
|
||||
'xpack.enterpriseSearch.workplaceSearch.sourceRow.remoteTooltip',
|
||||
{
|
||||
defaultMessage:
|
||||
"Remote sources rely on the source's search service directly, and no content is indexed with Workplace Search. Speed and integrity of results are functions of the third-party service's health and performance.",
|
||||
}
|
||||
);
|
||||
|
||||
export const SOURCE_ROW_SEARCHABLE_TOGGLE_LABEL = i18n.translate(
|
||||
'xpack.enterpriseSearch.workplaceSearch.sourceRow.searchableToggleLabel',
|
||||
{
|
||||
defaultMessage: 'Source searchable toggle',
|
||||
}
|
||||
);
|
||||
|
||||
export const SOURCE_ROW_DETAILS_LABEL = i18n.translate(
|
||||
'xpack.enterpriseSearch.workplaceSearch.sourceRow.detailsLabel',
|
||||
{
|
||||
defaultMessage: 'Details',
|
||||
}
|
||||
);
|
|
@ -35,15 +35,23 @@ describe('SourceRow', () => {
|
|||
expect(onToggle).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('renders "Fix" link', () => {
|
||||
it('renders "Re-authenticate" link', () => {
|
||||
const source = {
|
||||
...contentSources[0],
|
||||
status: 'error',
|
||||
errorReason: 'OAuth access token could not be refreshed',
|
||||
activities: [
|
||||
{
|
||||
status: 'error',
|
||||
details: [],
|
||||
event: '',
|
||||
time: '',
|
||||
},
|
||||
],
|
||||
};
|
||||
const wrapper = shallow(<SourceRow isOrganization source={source} />);
|
||||
|
||||
expect(wrapper.contains('Fix')).toBeTruthy();
|
||||
expect(wrapper.contains('Re-authenticate')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders loading icon when indexing', () => {
|
||||
|
|
|
@ -34,6 +34,14 @@ import { SourceIcon } from '../source_icon';
|
|||
|
||||
import './source_row.scss';
|
||||
|
||||
import {
|
||||
SOURCE_ROW_REAUTHENTICATE_STATUS_LINK_LABEL,
|
||||
SOURCE_ROW_REMOTE_LABEL,
|
||||
SOURCE_ROW_REMOTE_TOOLTIP,
|
||||
SOURCE_ROW_SEARCHABLE_TOGGLE_LABEL,
|
||||
SOURCE_ROW_DETAILS_LABEL,
|
||||
} from './constants';
|
||||
|
||||
// i18n is not needed here because this is only used to check against the server error, which
|
||||
// is not translated by the Kibana team at this time.
|
||||
const CREDENTIALS_REFRESH_NEEDED_PREFIX = 'OAuth access token could not be refreshed';
|
||||
|
@ -61,6 +69,7 @@ export const SourceRow: React.FC<SourceRowProps> = ({
|
|||
isFederatedSource,
|
||||
errorReason,
|
||||
allowsReauth,
|
||||
activities,
|
||||
},
|
||||
onSearchableToggle,
|
||||
isOrganization,
|
||||
|
@ -68,32 +77,29 @@ export const SourceRow: React.FC<SourceRowProps> = ({
|
|||
}) => {
|
||||
const isIndexing = status === statuses.INDEXING;
|
||||
const hasError = status === statuses.ERROR || status === statuses.DISCONNECTED;
|
||||
const showFix =
|
||||
isOrganization &&
|
||||
const showReauthenticate =
|
||||
hasError &&
|
||||
allowsReauth &&
|
||||
errorReason?.startsWith(CREDENTIALS_REFRESH_NEEDED_PREFIX);
|
||||
errorReason?.startsWith(CREDENTIALS_REFRESH_NEEDED_PREFIX) &&
|
||||
activities[0]?.status?.toLowerCase() === statuses.ERROR;
|
||||
|
||||
const rowClass = classNames({ 'source-row--error': hasError });
|
||||
|
||||
const fixLink = (
|
||||
const reauthenticateLink = (
|
||||
<EuiLinkTo
|
||||
to={getSourcesPath(
|
||||
`${ADD_SOURCE_PATH}/${serviceType}/reauthenticate?sourceId=${id}`,
|
||||
isOrganization
|
||||
)}
|
||||
>
|
||||
Fix
|
||||
{SOURCE_ROW_REAUTHENTICATE_STATUS_LINK_LABEL}
|
||||
</EuiLinkTo>
|
||||
);
|
||||
|
||||
const remoteTooltip = (
|
||||
<>
|
||||
<span>Remote</span>
|
||||
<EuiToolTip
|
||||
position="top"
|
||||
content="Remote sources rely on the source's search service directly, and no content is indexed with Workplace Search. Speed and integrity of results are functions of the third-party service's health and performance."
|
||||
>
|
||||
<span>{SOURCE_ROW_REMOTE_LABEL}</span>
|
||||
<EuiToolTip position="top" content={SOURCE_ROW_REMOTE_TOOLTIP}>
|
||||
<EuiIcon type="questionInCircle" />
|
||||
</EuiToolTip>
|
||||
</>
|
||||
|
@ -143,7 +149,7 @@ export const SourceRow: React.FC<SourceRowProps> = ({
|
|||
onChange={(e: EuiSwitchEvent) => onSearchableToggle(id, e.target.checked)}
|
||||
disabled={!supportedByLicense}
|
||||
compressed
|
||||
label="Source Searchable Toggle"
|
||||
label={SOURCE_ROW_SEARCHABLE_TOGGLE_LABEL}
|
||||
showLabel={false}
|
||||
data-test-subj="SourceSearchableToggle"
|
||||
/>
|
||||
|
@ -151,7 +157,7 @@ export const SourceRow: React.FC<SourceRowProps> = ({
|
|||
)}
|
||||
<EuiTableRowCell align="right">
|
||||
<EuiFlexGroup justifyContent="flexEnd" alignItems="center" gutterSize="s">
|
||||
{showFix && <EuiFlexItem grow={false}>{fixLink}</EuiFlexItem>}
|
||||
{showReauthenticate && <EuiFlexItem grow={false}>{reauthenticateLink}</EuiFlexItem>}
|
||||
<EuiFlexItem grow={false}>
|
||||
{showDetails && (
|
||||
<EuiLinkTo
|
||||
|
@ -159,7 +165,7 @@ export const SourceRow: React.FC<SourceRowProps> = ({
|
|||
data-test-subj="SourceDetailsLink"
|
||||
to={getContentSourcePath(SOURCE_DETAILS_PATH, id, !!isOrganization)}
|
||||
>
|
||||
Details
|
||||
{SOURCE_ROW_DETAILS_LABEL}
|
||||
</EuiLinkTo>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
|
|
|
@ -109,6 +109,7 @@ export interface ContentSourceDetails extends ContentSource {
|
|||
errorReason: string | null;
|
||||
allowsReauth: boolean;
|
||||
boost: number;
|
||||
activities: SourceActivity[];
|
||||
}
|
||||
|
||||
interface DescriptionList {
|
||||
|
|
|
@ -14,6 +14,8 @@ import React from 'react';
|
|||
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { EuiText } from '@elastic/eui';
|
||||
|
||||
import { ExampleResultDetailCard } from './example_result_detail_card';
|
||||
|
||||
describe('ExampleResultDetailCard', () => {
|
||||
|
@ -36,4 +38,18 @@ describe('ExampleResultDetailCard', () => {
|
|||
|
||||
expect(wrapper.find('[data-test-subj="DefaultUrlLabel"]')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('shows formatted value when date can be parsed', () => {
|
||||
const date = '2021-06-28';
|
||||
setMockValues({
|
||||
...exampleResult,
|
||||
searchResultConfig: { detailFields: [{ fieldName: 'date', label: 'Date' }] },
|
||||
exampleDocuments: [{ date }],
|
||||
});
|
||||
const wrapper = shallow(<ExampleResultDetailCard />);
|
||||
|
||||
expect(wrapper.find(EuiText).children().text()).toContain(
|
||||
new Date(Date.parse(date)).toLocaleString()
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -18,6 +18,11 @@ import { CustomSourceIcon } from './custom_source_icon';
|
|||
import { DisplaySettingsLogic } from './display_settings_logic';
|
||||
import { TitleField } from './title_field';
|
||||
|
||||
const getAsLocalDateTimeString = (str: string) => {
|
||||
const dateValue = Date.parse(str);
|
||||
return dateValue ? new Date(dateValue).toLocaleString() : null;
|
||||
};
|
||||
|
||||
export const ExampleResultDetailCard: React.FC = () => {
|
||||
const {
|
||||
sourceName,
|
||||
|
@ -60,20 +65,25 @@ export const ExampleResultDetailCard: React.FC = () => {
|
|||
</div>
|
||||
<div className="example-result-detail-card__content">
|
||||
{detailFields.length > 0 ? (
|
||||
detailFields.map(({ fieldName, label }, index) => (
|
||||
<div
|
||||
data-test-subj="DetailField"
|
||||
className="example-result-detail-card__field"
|
||||
key={index}
|
||||
>
|
||||
<EuiTitle size="xs">
|
||||
<h4>{label}</h4>
|
||||
</EuiTitle>
|
||||
<EuiText size="s" color="subdued">
|
||||
<div className="eui-textBreakWord">{result[fieldName]}</div>
|
||||
</EuiText>
|
||||
</div>
|
||||
))
|
||||
detailFields.map(({ fieldName, label }, index) => {
|
||||
const value = result[fieldName] as string;
|
||||
const dateValue = getAsLocalDateTimeString(value);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="example-result-detail-card__field"
|
||||
key={index}
|
||||
data-test-subj="DetailField"
|
||||
>
|
||||
<EuiTitle size="xs">
|
||||
<h4>{label}</h4>
|
||||
</EuiTitle>
|
||||
<EuiText size="s" color="subdued">
|
||||
<div className="eui-textBreakWord">{dateValue || value}</div>
|
||||
</EuiText>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<EuiSpacer size="m" />
|
||||
)}
|
||||
|
|
|
@ -198,12 +198,16 @@ export const Overview: React.FC = () => {
|
|||
{!custom && (
|
||||
<EuiTableRowCell>
|
||||
<EuiText size="xs">
|
||||
{status} {activityDetails && <StatusItem details={activityDetails} />}
|
||||
<small>
|
||||
{status} {activityDetails && <StatusItem details={activityDetails} />}
|
||||
</small>
|
||||
</EuiText>
|
||||
</EuiTableRowCell>
|
||||
)}
|
||||
<EuiTableRowCell align="right">
|
||||
<EuiText size="xs">{time}</EuiText>
|
||||
<EuiText size="xs">
|
||||
<small>{time}</small>
|
||||
</EuiText>
|
||||
</EuiTableRowCell>
|
||||
</EuiTableRow>
|
||||
))}
|
||||
|
@ -453,7 +457,7 @@ export const Overview: React.FC = () => {
|
|||
<ViewContentHeader title="Source overview" />
|
||||
|
||||
<EuiFlexGroup gutterSize="xl" alignItems="flexStart">
|
||||
<EuiFlexItem>
|
||||
<EuiFlexItem grow={8}>
|
||||
<EuiFlexGroup gutterSize="xl" direction="column">
|
||||
<EuiFlexItem>
|
||||
<DocumentSummary data-test-subj="DocumentSummary" />
|
||||
|
@ -465,7 +469,7 @@ export const Overview: React.FC = () => {
|
|||
)}
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexItem grow={7}>
|
||||
<EuiFlexGroup gutterSize="m" direction="column">
|
||||
<EuiFlexItem>{groups.length > 0 && groupsSummary}</EuiFlexItem>
|
||||
{details.length > 0 && <EuiFlexItem>{detailsSummary}</EuiFlexItem>}
|
||||
|
|
Loading…
Reference in a new issue