[APM] APM & Observability plugin lint improvements (#72702) (#73266)

* [APM] APM & Observability plugin lint improvements

This is a large change, but most of it is automatic `eslint --fix` changes.

* Apply the same ESLint ovderrides in APM and Observability plugins.
* Remove the `no-unused-vars` rule. We can turn on the TypeScript check if needed.
* Check both JS and TS files.
* Add a rule for react function component definitions
* Upgrade eslint-plugin-react to include that rule
This commit is contained in:
Nathan L Smith 2020-07-27 14:21:28 -05:00 committed by GitHub
parent aab54c0693
commit 74af87fcbb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
140 changed files with 822 additions and 731 deletions

View file

@ -775,19 +775,22 @@ module.exports = {
},
/**
* APM overrides
* APM and Observability overrides
*/
{
files: ['x-pack/plugins/apm/**/*.js'],
files: [
'x-pack/plugins/apm/**/*.{js,mjs,ts,tsx}',
'x-pack/plugins/observability/**/*.{js,mjs,ts,tsx}',
],
rules: {
'no-unused-vars': ['error', { ignoreRestSiblings: true }],
'no-console': ['warn', { allow: ['error'] }],
},
},
{
plugins: ['react-hooks'],
files: ['x-pack/plugins/apm/**/*.{ts,tsx}'],
rules: {
'react/function-component-definition': [
'warn',
{
namedComponents: 'function-declaration',
unnamedComponents: 'arrow-function',
},
],
'react-hooks/rules-of-hooks': 'error', // Checks rules of Hooks
'react-hooks/exhaustive-deps': ['error', { additionalHooks: '^useFetcher$' }],
},

View file

@ -430,7 +430,7 @@
"eslint-plugin-node": "^11.0.0",
"eslint-plugin-prefer-object-spread": "^1.2.1",
"eslint-plugin-prettier": "^3.1.3",
"eslint-plugin-react": "^7.17.0",
"eslint-plugin-react": "^7.20.3",
"eslint-plugin-react-hooks": "^4.0.4",
"eslint-plugin-react-perf": "^3.2.3",
"exit-hook": "^2.2.0",

View file

@ -51,13 +51,13 @@ describe('Inspector Data View', () => {
});
it('should render loading state', () => {
const component = mountWithIntl(<DataView.component title="Test Data" adapters={adapters} />);
const component = mountWithIntl(<DataView.component title="Test Data" adapters={adapters} />); // eslint-disable-line react/jsx-pascal-case
expect(component).toMatchSnapshot();
});
it('should render empty state', async () => {
const component = mountWithIntl(<DataView.component title="Test Data" adapters={adapters} />);
const component = mountWithIntl(<DataView.component title="Test Data" adapters={adapters} />); // eslint-disable-line react/jsx-pascal-case
const tabularLoader = Promise.resolve(null);
adapters.data.setTabularLoader(() => tabularLoader);
await tabularLoader;

View file

@ -29,6 +29,8 @@ module.exports = (on) => {
// readFileMaybe
on('task', {
// ESLint thinks this is a react component for some reason.
// eslint-disable-next-line react/function-component-definition
readFileMaybe(filename) {
if (fs.existsSync(filename)) {
return fs.readFileSync(filename, 'utf8');

View file

@ -37,7 +37,7 @@ const MainContainer = styled.div`
height: 100%;
`;
const App = () => {
function App() {
const [darkMode] = useUiSetting$<boolean>('theme:darkMode');
return (
@ -59,9 +59,9 @@ const App = () => {
</MainContainer>
</ThemeProvider>
);
};
}
const ApmAppRoot = ({
function ApmAppRoot({
core,
deps,
routerHistory,
@ -71,7 +71,7 @@ const ApmAppRoot = ({
deps: ApmPluginSetupDeps;
routerHistory: typeof history;
config: ConfigSchema;
}) => {
}) {
const i18nCore = core.i18n;
const plugins = deps;
const apmPluginContextValue = {
@ -111,7 +111,7 @@ const ApmAppRoot = ({
</AlertsContextProvider>
</ApmPluginContext.Provider>
);
};
}
/**
* This module is rendered asynchronously in the Kibana platform.

View file

@ -53,7 +53,7 @@ interface Props {
items: ErrorGroupListAPIResponse;
}
const ErrorGroupList: React.FC<Props> = (props) => {
function ErrorGroupList(props: Props) {
const { items } = props;
const { urlParams } = useUrlParams();
const { serviceName } = urlParams;
@ -213,6 +213,6 @@ const ErrorGroupList: React.FC<Props> = (props) => {
sortItems={false}
/>
);
};
}
export { ErrorGroupList };

View file

@ -22,7 +22,7 @@ import { LocalUIFilters } from '../../shared/LocalUIFilters';
import { ErrorDistribution } from '../ErrorGroupDetails/Distribution';
import { ErrorGroupList } from './List';
const ErrorGroupOverview: React.FC = () => {
function ErrorGroupOverview() {
const { urlParams, uiFilters } = useUrlParams();
const { serviceName, start, end, sortField, sortDirection } = urlParams;
@ -123,6 +123,6 @@ const ErrorGroupOverview: React.FC = () => {
</EuiFlexGroup>
</>
);
};
}
export { ErrorGroupOverview };

View file

@ -20,11 +20,11 @@ interface Props {
onBreakdownChange: (values: BreakdownItem[]) => void;
}
export const BreakdownFilter = ({
export function BreakdownFilter({
id,
selectedBreakdowns,
onBreakdownChange,
}: Props) => {
}: Props) {
const categories: BreakdownItem[] = [
{
name: 'Browser',
@ -65,4 +65,4 @@ export const BreakdownFilter = ({
}}
/>
);
};
}

View file

@ -22,12 +22,12 @@ export interface BreakdownGroupProps {
onChange: (values: BreakdownItem[]) => void;
}
export const BreakdownGroup = ({
export function BreakdownGroup({
id,
disabled,
onChange,
items,
}: BreakdownGroupProps) => {
}: BreakdownGroupProps) {
const [isOpen, setIsOpen] = useState<boolean>(false);
const [activeItems, setActiveItems] = useState<BreakdownItem[]>(items);
@ -97,4 +97,4 @@ export const BreakdownGroup = ({
</EuiPopover>
</EuiFilterGroup>
);
};
}

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { FC, HTMLAttributes } from 'react';
import React, { HTMLAttributes, ReactNode } from 'react';
import {
EuiErrorBoundary,
EuiFlexGroup,
@ -13,6 +13,7 @@ import {
} from '@elastic/eui';
interface Props {
children?: ReactNode;
/**
* Height for the chart
*/
@ -27,12 +28,12 @@ interface Props {
'aria-label'?: string;
}
export const ChartWrapper: FC<Props> = ({
export function ChartWrapper({
loading = false,
height = '100%',
children,
...rest
}) => {
}: Props) {
const opacity = loading === true ? 0.3 : 1;
return (
@ -60,4 +61,4 @@ export const ChartWrapper: FC<Props> = ({
)}
</EuiErrorBoundary>
);
};
}

View file

@ -70,6 +70,7 @@ export function PageLoadDistChart({
onPercentileChange(minX, maxX);
};
// eslint-disable-next-line react/function-component-definition
const headerFormatter: TooltipValueFormatter = (tooltip: TooltipValue) => {
return (
<div>

View file

@ -29,7 +29,7 @@ interface Props {
}>;
}
export const VisitorBreakdownChart = ({ options }: Props) => {
export function VisitorBreakdownChart({ options }: Props) {
const [darkMode] = useUiSetting$<boolean>('theme:darkMode');
return (
@ -93,4 +93,4 @@ export const VisitorBreakdownChart = ({ options }: Props) => {
</Chart>
</ChartWrapper>
);
};
}

View file

@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { FC, useEffect } from 'react';
import { CurveType, LineSeries, ScaleType } from '@elastic/charts';
import React, { useEffect } from 'react';
import { PercentileRange } from './index';
import { useBreakdowns } from './use_breakdowns';
@ -16,12 +16,12 @@ interface Props {
onLoadingChange: (loading: boolean) => void;
}
export const BreakdownSeries: FC<Props> = ({
export function BreakdownSeries({
field,
value,
percentileRange,
onLoadingChange,
}) => {
}: Props) {
const { data, status } = useBreakdowns({
field,
value,
@ -47,4 +47,4 @@ export const BreakdownSeries: FC<Props> = ({
))}
</>
);
};
}

View file

@ -33,7 +33,7 @@ const PercentileMarker = styled.span`
bottom: 205px;
`;
export const PercentileAnnotations = ({ percentiles }: Props) => {
export function PercentileAnnotations({ percentiles }: Props) {
const dataValues = generateAnnotationData(percentiles) ?? [];
const style: Partial<LineAnnotationStyle> = {
@ -44,17 +44,17 @@ export const PercentileAnnotations = ({ percentiles }: Props) => {
},
};
const PercentileTooltip = ({
function PercentileTooltip({
annotation,
}: {
annotation: LineAnnotationDatum;
}) => {
}) {
return (
<span data-cy="percentileTooltipTitle">
{annotation.details}th Percentile
</span>
);
};
}
return (
<>
@ -82,4 +82,4 @@ export const PercentileAnnotations = ({ percentiles }: Props) => {
))}
</>
);
};
}

View file

@ -24,7 +24,7 @@ export interface PercentileRange {
max?: number | null;
}
export const PageLoadDistribution = () => {
export function PageLoadDistribution() {
const { urlParams, uiFilters } = useUrlParams();
const { start, end, serviceName } = urlParams;
@ -115,4 +115,4 @@ export const PageLoadDistribution = () => {
/>
</div>
);
};
}

View file

@ -13,7 +13,7 @@ import { BreakdownFilter } from '../Breakdowns/BreakdownFilter';
import { PageViewsChart } from '../Charts/PageViewsChart';
import { BreakdownItem } from '../../../../../typings/ui_filters';
export const PageViewsTrend = () => {
export function PageViewsTrend() {
const { urlParams, uiFilters } = useUrlParams();
const { start, end, serviceName } = urlParams;
@ -68,4 +68,4 @@ export const PageViewsTrend = () => {
<PageViewsChart data={data} loading={status !== 'success'} />
</div>
);
};
}

View file

@ -18,7 +18,7 @@ import { PageLoadDistribution } from './PageLoadDistribution';
import { I18LABELS } from './translations';
import { VisitorBreakdown } from './VisitorBreakdown';
export const RumDashboard = () => {
export function RumDashboard() {
return (
<EuiFlexGroup direction="column" gutterSize="s">
<EuiFlexItem>
@ -54,4 +54,4 @@ export const RumDashboard = () => {
</EuiFlexItem>
</EuiFlexGroup>
);
};
}

View file

@ -5,16 +5,18 @@
*/
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import React from 'react';
import React, { ReactNode } from 'react';
import { DatePicker } from '../../../shared/DatePicker';
export const RumHeader: React.FC = ({ children }) => (
<>
<EuiFlexGroup alignItems="center" gutterSize="s">
<EuiFlexItem>{children}</EuiFlexItem>
<EuiFlexItem grow={false}>
<DatePicker />
</EuiFlexItem>
</EuiFlexGroup>
</>
);
export function RumHeader({ children }: { children: ReactNode }) {
return (
<>
<EuiFlexGroup alignItems="center" gutterSize="s">
<EuiFlexItem>{children}</EuiFlexItem>
<EuiFlexItem grow={false}>
<DatePicker />
</EuiFlexItem>
</EuiFlexGroup>
</>
);
}

View file

@ -11,7 +11,7 @@ import { VisitorBreakdownLabel } from '../translations';
import { useFetcher } from '../../../../hooks/useFetcher';
import { useUrlParams } from '../../../../hooks/useUrlParams';
export const VisitorBreakdown = () => {
export function VisitorBreakdown() {
const { urlParams, uiFilters } = useUrlParams();
const { start, end, serviceName } = urlParams;
@ -62,4 +62,4 @@ export const VisitorBreakdown = () => {
</EuiFlexGroup>
</>
);
};
}

View file

@ -4,32 +4,38 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { FunctionComponent } from 'react';
import { act, wait } from '@testing-library/react';
import cytoscape from 'cytoscape';
import { CytoscapeContext } from './Cytoscape';
import { EmptyBanner } from './EmptyBanner';
import React, { ReactNode } from 'react';
import { MockApmPluginContextWrapper } from '../../../context/ApmPluginContext/MockApmPluginContext';
import { renderWithTheme } from '../../../utils/testHelpers';
import { CytoscapeContext } from './Cytoscape';
import { EmptyBanner } from './EmptyBanner';
const cy = cytoscape({});
const wrapper: FunctionComponent = ({ children }) => (
<MockApmPluginContextWrapper>
<CytoscapeContext.Provider value={cy}>{children}</CytoscapeContext.Provider>
</MockApmPluginContextWrapper>
);
function wrapper({ children }: { children: ReactNode }) {
return (
<MockApmPluginContextWrapper>
<CytoscapeContext.Provider value={cy}>
{children}
</CytoscapeContext.Provider>
</MockApmPluginContextWrapper>
);
}
describe('EmptyBanner', () => {
describe('when cy is undefined', () => {
it('renders null', () => {
const noCytoscapeWrapper: FunctionComponent = ({ children }) => (
<MockApmPluginContextWrapper>
<CytoscapeContext.Provider value={undefined}>
{children}
</CytoscapeContext.Provider>
</MockApmPluginContextWrapper>
);
function noCytoscapeWrapper({ children }: { children: ReactNode }) {
return (
<MockApmPluginContextWrapper>
<CytoscapeContext.Provider value={undefined}>
{children}
</CytoscapeContext.Provider>
</MockApmPluginContextWrapper>
);
}
const component = renderWithTheme(<EmptyBanner />, {
wrapper: noCytoscapeWrapper,
});

View file

@ -34,26 +34,28 @@ interface Props {
percentageLoaded: number;
}
export const LoadingOverlay = ({ isLoading, percentageLoaded }: Props) => (
<Container>
{isLoading && (
<Overlay>
<ProgressBarContainer>
<EuiProgress
value={percentageLoaded}
max={100}
color="primary"
size="m"
/>
</ProgressBarContainer>
<EuiSpacer size="s" />
<EuiText size="s" textAlign="center">
{i18n.translate('xpack.apm.loadingServiceMap', {
defaultMessage:
'Loading service map... This might take a short while.',
})}
</EuiText>
</Overlay>
)}
</Container>
);
export function LoadingOverlay({ isLoading, percentageLoaded }: Props) {
return (
<Container>
{isLoading && (
<Overlay>
<ProgressBarContainer>
<EuiProgress
value={percentageLoaded}
max={100}
color="primary"
size="m"
/>
</ProgressBarContainer>
<EuiSpacer size="s" />
<EuiText size="s" textAlign="center">
{i18n.translate('xpack.apm.loadingServiceMap', {
defaultMessage:
'Loading service map... This might take a short while.',
})}
</EuiText>
</Overlay>
)}
</Container>
);
}

View file

@ -34,20 +34,21 @@ interface ContentsProps {
// @ts-ignore `documentMode` is not recognized as a valid property of `document`.
const isIE11 = !!window.MSInputMethodContext && !!document.documentMode;
const FlexColumnGroup = (props: {
function FlexColumnGroup(props: {
children: React.ReactNode;
style: React.CSSProperties;
direction: 'column';
gutterSize: 's';
}) => {
}) {
if (isIE11) {
const { direction, gutterSize, ...rest } = props;
return <div {...rest} />;
}
return <EuiFlexGroup {...props} />;
};
const FlexColumnItem = (props: { children: React.ReactNode }) =>
isIE11 ? <div {...props} /> : <EuiFlexItem {...props} />;
}
function FlexColumnItem(props: { children: React.ReactNode }) {
return isIE11 ? <div {...props} /> : <EuiFlexItem {...props} />;
}
export function Contents({
selectedNodeData,

View file

@ -5,7 +5,7 @@
*/
import { render } from '@testing-library/react';
import React, { FunctionComponent } from 'react';
import React, { ReactNode } from 'react';
import { License } from '../../../../../licensing/common/license';
import { LicenseContext } from '../../../context/LicenseContext';
import { ServiceMap } from './';
@ -22,13 +22,13 @@ const expiredLicense = new License({
},
});
const Wrapper: FunctionComponent = ({ children }) => {
function Wrapper({ children }: { children?: ReactNode }) {
return (
<LicenseContext.Provider value={expiredLicense}>
<MockApmPluginContextWrapper>{children}</MockApmPluginContextWrapper>
</LicenseContext.Provider>
);
};
}
describe('ServiceMap', () => {
describe('with an inactive license', () => {

View file

@ -29,7 +29,7 @@ interface ServiceMapProps {
serviceName?: string;
}
export const ServiceMap = ({ serviceName }: ServiceMapProps) => {
export function ServiceMap({ serviceName }: ServiceMapProps) {
const theme = useTheme();
const license = useLicense();
const { urlParams } = useUrlParams();
@ -101,4 +101,4 @@ export const ServiceMap = ({ serviceName }: ServiceMapProps) => {
</EuiFlexItem>
</EuiFlexGroup>
);
};
}

View file

@ -36,7 +36,7 @@ const ServiceNodeName = styled.div`
${truncate(px(8 * unit))}
`;
const ServiceNodeOverview = () => {
function ServiceNodeOverview() {
const { uiFilters, urlParams } = useUrlParams();
const { serviceName, start, end } = urlParams;
@ -182,6 +182,6 @@ const ServiceNodeOverview = () => {
</EuiFlexGroup>
</>
);
};
}
export { ServiceNodeOverview };

View file

@ -38,7 +38,7 @@ interface Props {
refetch: () => void;
}
export const AgentConfigurationList = ({ status, data, refetch }: Props) => {
export function AgentConfigurationList({ status, data, refetch }: Props) {
const theme = useTheme();
const [configToBeDeleted, setConfigToBeDeleted] = useState<Config | null>(
null
@ -219,4 +219,4 @@ export const AgentConfigurationList = ({ status, data, refetch }: Props) => {
/>
</>
);
};
}

View file

@ -7,15 +7,13 @@ import React from 'react';
import { EuiButton } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
export const CreateCustomLinkButton = ({
onClick,
}: {
onClick: () => void;
}) => (
<EuiButton color="primary" fill onClick={onClick}>
{i18n.translate(
'xpack.apm.settings.customizeUI.customLink.createCustomLink',
{ defaultMessage: 'Create custom link' }
)}
</EuiButton>
);
export function CreateCustomLinkButton({ onClick }: { onClick: () => void }) {
return (
<EuiButton color="primary" fill onClick={onClick}>
{i18n.translate(
'xpack.apm.settings.customizeUI.customLink.createCustomLink',
{ defaultMessage: 'Create custom link' }
)}
</EuiButton>
);
}

View file

@ -9,8 +9,14 @@ import { ElasticDocsLink } from '../../../../../shared/Links/ElasticDocsLink';
interface Props {
label: string;
}
export const Documentation = ({ label }: Props) => (
<ElasticDocsLink section="/kibana" path="/custom-links.html" target="_blank">
{label}
</ElasticDocsLink>
);
export function Documentation({ label }: Props) {
return (
<ElasticDocsLink
section="/kibana"
path="/custom-links.html"
target="_blank"
>
{label}
</ElasticDocsLink>
);
}

View file

@ -26,13 +26,13 @@ import {
getSelectOptions,
} from './helper';
export const FiltersSection = ({
export function FiltersSection({
filters,
onChangeFilters,
}: {
filters: Filter[];
onChangeFilters: (filters: Filter[]) => void;
}) => {
}) {
const onChangeFilter = (
key: Filter['key'],
value: Filter['value'],
@ -147,25 +147,27 @@ export const FiltersSection = ({
/>
</>
);
};
}
const AddFilterButton = ({
function AddFilterButton({
onClick,
isDisabled,
}: {
onClick: () => void;
isDisabled: boolean;
}) => (
<EuiButtonEmpty
iconType="plusInCircle"
onClick={onClick}
disabled={isDisabled}
>
{i18n.translate(
'xpack.apm.settings.customizeUI.customLink.flyout.filters.addAnotherFilter',
{
defaultMessage: 'Add another filter',
}
)}
</EuiButtonEmpty>
);
}) {
return (
<EuiButtonEmpty
iconType="plusInCircle"
onClick={onClick}
disabled={isDisabled}
>
{i18n.translate(
'xpack.apm.settings.customizeUI.customLink.flyout.filters.addAnotherFilter',
{
defaultMessage: 'Add another filter',
}
)}
</EuiButtonEmpty>
);
}

View file

@ -14,7 +14,7 @@ import {
import { i18n } from '@kbn/i18n';
import { DeleteButton } from './DeleteButton';
export const FlyoutFooter = ({
export function FlyoutFooter({
onClose,
isSaving,
onDelete,
@ -26,7 +26,7 @@ export const FlyoutFooter = ({
onDelete: () => void;
customLinkId?: string;
isSaveButtonEnabled: boolean;
}) => {
}) {
return (
<EuiFlyoutFooter>
<EuiFlexGroup justifyContent="spaceBetween">
@ -61,4 +61,4 @@ export const FlyoutFooter = ({
</EuiFlexGroup>
</EuiFlyoutFooter>
);
};
}

View file

@ -41,7 +41,7 @@ const fetchTransaction = debounce(
const getTextColor = (value?: string) => (value ? 'default' : 'subdued');
export const LinkPreview = ({ label, url, filters }: Props) => {
export function LinkPreview({ label, url, filters }: Props) {
const [transaction, setTransaction] = useState<Transaction | undefined>();
useEffect(() => {
@ -128,4 +128,4 @@ export const LinkPreview = ({ label, url, filters }: Props) => {
</EuiFlexGroup>
</EuiPanel>
);
};
}

View file

@ -31,12 +31,7 @@ interface Props {
onChangeUrl: (url: string) => void;
}
export const LinkSection = ({
label,
onChangeLabel,
url,
onChangeUrl,
}: Props) => {
export function LinkSection({ label, onChangeLabel, url, onChangeUrl }: Props) {
const inputFields: InputField[] = [
{
name: 'label',
@ -145,4 +140,4 @@ export const LinkSection = ({
})}
</>
);
};
}

View file

@ -37,13 +37,13 @@ interface Props {
const filtersEmptyState: Filter[] = [{ key: '', value: '' }];
export const CustomLinkFlyout = ({
export function CustomLinkFlyout({
onClose,
onSave,
onDelete,
defaults,
customLinkId,
}: Props) => {
}: Props) {
const { toasts } = useApmPluginContext().core.notifications;
const [isSaving, setIsSaving] = useState(false);
@ -139,4 +139,4 @@ export const CustomLinkFlyout = ({
</form>
</EuiPortal>
);
};
}

View file

@ -24,10 +24,7 @@ interface Props {
onCustomLinkSelected: (customLink: CustomLink) => void;
}
export const CustomLinkTable = ({
items = [],
onCustomLinkSelected,
}: Props) => {
export function CustomLinkTable({ items = [], onCustomLinkSelected }: Props) {
const [searchTerm, setSearchTerm] = useState('');
const columns = [
@ -121,20 +118,22 @@ export const CustomLinkTable = ({
/>
</>
);
};
}
const NoResultFound = ({ value }: { value: string }) => (
<EuiFlexGroup justifyContent="spaceAround">
<EuiFlexItem grow={false}>
<EuiText size="s">
{i18n.translate(
'xpack.apm.settings.customizeUI.customLink.table.noResultFound',
{
defaultMessage: `No results for "{value}".`,
values: { value },
}
)}
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
);
function NoResultFound({ value }: { value: string }) {
return (
<EuiFlexGroup justifyContent="spaceAround">
<EuiFlexItem grow={false}>
<EuiText size="s">
{i18n.translate(
'xpack.apm.settings.customizeUI.customLink.table.noResultFound',
{
defaultMessage: `No results for "{value}".`,
values: { value },
}
)}
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
);
}

View file

@ -8,11 +8,11 @@ import { i18n } from '@kbn/i18n';
import React from 'react';
import { CreateCustomLinkButton } from './CreateCustomLinkButton';
export const EmptyPrompt = ({
export function EmptyPrompt({
onCreateCustomLinkClick,
}: {
onCreateCustomLinkClick: () => void;
}) => {
}) {
return (
<EuiEmptyPrompt
iconType="link"
@ -43,4 +43,4 @@ export const EmptyPrompt = ({
actions={<CreateCustomLinkButton onClick={onCreateCustomLinkClick} />}
/>
);
};
}

View file

@ -7,34 +7,36 @@ import { EuiFlexGroup, EuiFlexItem, EuiIconTip, EuiTitle } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
export const Title = () => (
<EuiFlexGroup alignItems="center">
<EuiFlexItem grow={false}>
<EuiTitle size="s">
<EuiFlexGroup alignItems="center" gutterSize="s" responsive={false}>
<EuiFlexItem grow={false}>
<h1>
{i18n.translate('xpack.apm.settings.customizeUI.customLink', {
defaultMessage: 'Custom Links',
})}
</h1>
</EuiFlexItem>
export function Title() {
return (
<EuiFlexGroup alignItems="center">
<EuiFlexItem grow={false}>
<EuiTitle size="s">
<EuiFlexGroup alignItems="center" gutterSize="s" responsive={false}>
<EuiFlexItem grow={false}>
<h1>
{i18n.translate('xpack.apm.settings.customizeUI.customLink', {
defaultMessage: 'Custom Links',
})}
</h1>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiIconTip
type="iInCircle"
position="top"
content={i18n.translate(
'xpack.apm.settings.customizeUI.customLink.info',
{
defaultMessage:
'These links will be shown in the Actions context menu for transactions.',
}
)}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiTitle>
</EuiFlexItem>
</EuiFlexGroup>
);
<EuiFlexItem grow={false}>
<EuiIconTip
type="iInCircle"
position="top"
content={i18n.translate(
'xpack.apm.settings.customizeUI.customLink.info',
{
defaultMessage:
'These links will be shown in the Actions context menu for transactions.',
}
)}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiTitle>
</EuiFlexItem>
</EuiFlexGroup>
);
}

View file

@ -18,7 +18,7 @@ import { Title } from './Title';
import { CreateCustomLinkButton } from './CreateCustomLinkButton';
import { LicensePrompt } from '../../../../shared/LicensePrompt';
export const CustomLinkOverview = () => {
export function CustomLinkOverview() {
const license = useLicense();
const hasValidLicense = license?.isActive && license?.hasAtLeast('gold');
@ -107,4 +107,4 @@ export const CustomLinkOverview = () => {
</EuiPanel>
</>
);
};
}

View file

@ -9,7 +9,7 @@ import { EuiTitle, EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { CustomLinkOverview } from './CustomLink';
export const CustomizeUI = () => {
export function CustomizeUI() {
return (
<>
<EuiTitle size="l">
@ -23,4 +23,4 @@ export const CustomizeUI = () => {
<CustomLinkOverview />
</>
);
};
}

View file

@ -31,11 +31,11 @@ interface Props {
onCreateJobSuccess: () => void;
onCancel: () => void;
}
export const AddEnvironments = ({
export function AddEnvironments({
currentEnvironments,
onCreateJobSuccess,
onCancel,
}: Props) => {
}: Props) {
const { notifications, application } = useApmPluginContext().core;
const canCreateJob = !!application.capabilities.ml.canCreateJob;
const { toasts } = notifications;
@ -175,4 +175,4 @@ export const AddEnvironments = ({
<EuiSpacer size="l" />
</EuiPanel>
);
};
}

View file

@ -28,7 +28,7 @@ const DEFAULT_VALUE: AnomalyDetectionApiResponse = {
errorCode: undefined,
};
export const AnomalyDetection = () => {
export function AnomalyDetection() {
const plugin = useApmPluginContext();
const canGetJobs = !!plugin.core.application.capabilities.ml.canGetJobs;
const license = useLicense();
@ -112,4 +112,4 @@ export const AnomalyDetection = () => {
)}
</>
);
};
}

View file

@ -65,7 +65,7 @@ interface Props {
status: FETCH_STATUS;
onAddEnvironments: () => void;
}
export const JobsList = ({ data, status, onAddEnvironments }: Props) => {
export function JobsList({ data, status, onAddEnvironments }: Props) {
const { jobs, hasLegacyJobs, errorCode } = data;
return (
@ -127,7 +127,7 @@ export const JobsList = ({ data, status, onAddEnvironments }: Props) => {
{hasLegacyJobs && <LegacyJobsCallout />}
</EuiPanel>
);
};
}
function getNoItemsMessage({
status,

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import React, { ReactNode } from 'react';
import { i18n } from '@kbn/i18n';
import {
EuiButtonEmpty,
@ -17,7 +17,7 @@ import { HomeLink } from '../../shared/Links/apm/HomeLink';
import { useLocation } from '../../../hooks/useLocation';
import { getAPMHref } from '../../shared/Links/apm/APMLink';
export const Settings: React.FC = (props) => {
export function Settings(props: { children: ReactNode }) {
const { search, pathname } = useLocation();
return (
<>
@ -84,4 +84,4 @@ export const Settings: React.FC = (props) => {
</EuiPage>
</>
);
};
}

View file

@ -58,7 +58,7 @@ const redirectToTracePage = ({
},
});
export const TraceLink = () => {
export function TraceLink() {
const { urlParams } = useUrlParams();
const { traceIdLink: traceId, rangeFrom, rangeTo } = urlParams;
@ -93,4 +93,4 @@ export const TraceLink = () => {
<EuiEmptyPrompt iconType="apmTrace" title={<h2>Fetching trace...</h2>} />
</CentralizedContainer>
);
};
}

View file

@ -7,19 +7,19 @@
import { EuiIconTip, EuiTitle } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import d3 from 'd3';
import React, { FunctionComponent, useCallback } from 'react';
import { isEmpty } from 'lodash';
import React, { useCallback } from 'react';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { TransactionDistributionAPIResponse } from '../../../../../server/lib/transactions/distribution';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { IBucket } from '../../../../../server/lib/transactions/distribution/get_buckets/transform';
import { IUrlParams } from '../../../../context/UrlParamsContext/types';
import { getDurationFormatter } from '../../../../utils/formatters';
import { history } from '../../../../utils/history';
// @ts-ignore
import Histogram from '../../../shared/charts/Histogram';
import { EmptyMessage } from '../../../shared/EmptyMessage';
import { fromQuery, toQuery } from '../../../shared/Links/url_helpers';
import { history } from '../../../../utils/history';
import { LoadingStatePrompt } from '../../../shared/LoadingStatePrompt';
interface IChartPoint {
@ -99,9 +99,7 @@ interface Props {
bucketIndex: number;
}
export const TransactionDistribution: FunctionComponent<Props> = (
props: Props
) => {
export function TransactionDistribution(props: Props) {
const {
distribution,
urlParams: { transactionType },
@ -211,4 +209,4 @@ export const TransactionDistribution: FunctionComponent<Props> = (
/>
</div>
);
};
}

View file

@ -12,21 +12,23 @@ interface Props {
count: number;
}
export const ErrorCount = ({ count }: Props) => (
<EuiText size="xs">
<h4>
<EuiTextColor
color="danger"
onClick={(e: React.MouseEvent) => {
e.stopPropagation();
}}
>
{i18n.translate('xpack.apm.transactionDetails.errorCount', {
defaultMessage:
'{errorCount, number} {errorCount, plural, one {Error} other {Errors}}',
values: { errorCount: count },
})}
</EuiTextColor>
</h4>
</EuiText>
);
export function ErrorCount({ count }: Props) {
return (
<EuiText size="xs">
<h4>
<EuiTextColor
color="danger"
onClick={(e: React.MouseEvent) => {
e.stopPropagation();
}}
>
{i18n.translate('xpack.apm.transactionDetails.errorCount', {
defaultMessage:
'{errorCount, number} {errorCount, plural, one {Error} other {Errors}}',
values: { errorCount: count },
})}
</EuiTextColor>
</h4>
</EuiText>
);
}

View file

@ -6,7 +6,7 @@
import { EuiIcon, EuiLink } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { Fragment, useEffect, useRef, useState } from 'react';
import React, { Fragment, ReactNode, useEffect, useRef, useState } from 'react';
import styled from 'styled-components';
import { px, units } from '../../../../../../../style/variables';
@ -16,13 +16,11 @@ const ToggleButtonContainer = styled.div`
`;
interface Props {
children: ReactNode;
previewHeight: number;
}
export const TruncateHeightSection: React.FC<Props> = ({
children,
previewHeight,
}) => {
export function TruncateHeightSection({ children, previewHeight }: Props) {
const contentContainerEl = useRef<HTMLDivElement>(null);
const [showToggle, setShowToggle] = useState(true);
@ -73,4 +71,4 @@ export const TruncateHeightSection: React.FC<Props> = ({
) : null}
</Fragment>
);
};
}

View file

@ -15,12 +15,13 @@ interface Props {
location: Location;
toggleFlyout: ({ location }: { location: Location }) => void;
}
export const WaterfallFlyout: React.FC<Props> = ({
export function WaterfallFlyout({
waterfallItemId,
waterfall,
location,
toggleFlyout,
}) => {
}: Props) {
const currentItem = waterfall.items.find(
(item) => item.id === waterfallItemId
);
@ -58,4 +59,4 @@ export const WaterfallFlyout: React.FC<Props> = ({
default:
return null;
}
};
}

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import React, { ReactNode } from 'react';
import styled from 'styled-components';
import { EuiIcon, EuiText, EuiTitle, EuiToolTip } from '@elastic/eui';
@ -109,13 +109,11 @@ function PrefixIcon({ item }: { item: IWaterfallItem }) {
}
interface SpanActionToolTipProps {
children: ReactNode;
item?: IWaterfallItem;
}
const SpanActionToolTip: React.FC<SpanActionToolTipProps> = ({
item,
children,
}) => {
function SpanActionToolTip({ item, children }: SpanActionToolTipProps) {
if (item?.docType === 'span') {
return (
<EuiToolTip content={`${item.doc.span.subtype}.${item.doc.span.action}`}>
@ -124,7 +122,7 @@ const SpanActionToolTip: React.FC<SpanActionToolTipProps> = ({
);
}
return <>{children}</>;
};
}
function Duration({ item }: { item: IWaterfallItem }) {
return (

View file

@ -67,12 +67,12 @@ interface Props {
exceedsMax: boolean;
}
export const Waterfall: React.FC<Props> = ({
export function Waterfall({
waterfall,
exceedsMax,
waterfallItemId,
location,
}) => {
}: Props) {
const itemContainerHeight = 58; // TODO: This is a nasty way to calculate the height of the svg element. A better approach should be found
const waterfallHeight = itemContainerHeight * waterfall.items.length;
@ -81,7 +81,7 @@ export const Waterfall: React.FC<Props> = ({
const agentMarks = getAgentMarks(waterfall.entryTransaction);
const errorMarks = getErrorMarks(waterfall.errorItems, serviceColors);
const renderWaterfallItem = (item: IWaterfallItem) => {
function renderWaterfallItem(item: IWaterfallItem) {
const errorCount =
item.docType === 'transaction'
? waterfall.errorsPerTransaction[item.doc.transaction.id]
@ -99,7 +99,7 @@ export const Waterfall: React.FC<Props> = ({
onClick={() => toggleFlyout({ item, location })}
/>
);
};
}
return (
<Container>
@ -134,4 +134,4 @@ export const Waterfall: React.FC<Props> = ({
/>
</Container>
);
};
}

View file

@ -37,14 +37,14 @@ interface Props {
traceSamples: IBucket['samples'];
}
export const WaterfallWithSummmary: React.FC<Props> = ({
export function WaterfallWithSummmary({
urlParams,
location,
waterfall,
exceedsMax,
isLoading,
traceSamples,
}) => {
}: Props) {
const [sampleActivePage, setSampleActivePage] = useState(0);
useEffect(() => {
@ -135,4 +135,4 @@ export const WaterfallWithSummmary: React.FC<Props> = ({
/>
</EuiPanel>
);
};
}

View file

@ -5,29 +5,31 @@
*/
import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
import React from 'react';
import React, { ReactNode } from 'react';
import { KueryBar } from '../KueryBar';
import { DatePicker } from '../DatePicker';
import { EnvironmentFilter } from '../EnvironmentFilter';
export const ApmHeader: React.FC = ({ children }) => (
<>
<EuiFlexGroup alignItems="center" gutterSize="s">
<EuiFlexItem>{children}</EuiFlexItem>
<EuiFlexItem grow={false}>
<DatePicker />
</EuiFlexItem>
</EuiFlexGroup>
export function ApmHeader({ children }: { children: ReactNode }) {
return (
<>
<EuiFlexGroup alignItems="center" gutterSize="s">
<EuiFlexItem>{children}</EuiFlexItem>
<EuiFlexItem grow={false}>
<DatePicker />
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer />
<EuiSpacer />
<EuiFlexGroup alignItems="flexStart" gutterSize="s">
<EuiFlexItem grow={3}>
<KueryBar />
</EuiFlexItem>
<EuiFlexItem grow={1}>
<EnvironmentFilter />
</EuiFlexItem>
</EuiFlexGroup>
</>
);
<EuiFlexGroup alignItems="flexStart" gutterSize="s">
<EuiFlexItem grow={3}>
<KueryBar />
</EuiFlexItem>
<EuiFlexItem grow={1}>
<EnvironmentFilter />
</EuiFlexItem>
</EuiFlexGroup>
</>
);
}

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import React, { ReactNode } from 'react';
import { LocationProvider } from '../../../../context/LocationContext';
import {
UrlParamsContext,
@ -21,18 +21,24 @@ import { MockApmPluginContextWrapper } from '../../../../context/ApmPluginContex
const mockHistoryPush = jest.spyOn(history, 'push');
const mockRefreshTimeRange = jest.fn();
const MockUrlParamsProvider: React.FC<{
function MockUrlParamsProvider({
params = {},
children,
}: {
children: ReactNode;
params?: IUrlParams;
}> = ({ params = {}, children }) => (
<UrlParamsContext.Provider
value={{
urlParams: params,
refreshTimeRange: mockRefreshTimeRange,
uiFilters: useUiFilters(params),
}}
children={children}
/>
);
}) {
return (
<UrlParamsContext.Provider
value={{
urlParams: params,
refreshTimeRange: mockRefreshTimeRange,
uiFilters: useUiFilters(params),
}}
children={children}
/>
);
}
function mountDatePicker(params?: IUrlParams) {
return mount(

View file

@ -14,7 +14,7 @@ interface Props {
hideSubheading?: boolean;
}
const EmptyMessage: React.FC<Props> = ({
function EmptyMessage({
heading = i18n.translate('xpack.apm.emptyMessage.noDataFoundLabel', {
defaultMessage: 'No data found.',
}),
@ -22,7 +22,7 @@ const EmptyMessage: React.FC<Props> = ({
defaultMessage: 'Try another time range or reset the search filter.',
}),
hideSubheading = false,
}) => {
}: Props) {
return (
<EuiEmptyPrompt
titleSize="s"
@ -30,6 +30,6 @@ const EmptyMessage: React.FC<Props> = ({
body={!hideSubheading && subheading}
/>
);
};
}
export { EmptyMessage };

View file

@ -11,7 +11,7 @@ import { EuiBadge, EuiToolTip } from '@elastic/eui';
interface Props {
environments: string[];
}
export const EnvironmentBadge: React.FC<Props> = ({ environments = [] }) => {
export function EnvironmentBadge({ environments = [] }: Props) {
if (environments.length < 3) {
return (
<>
@ -42,4 +42,4 @@ export const EnvironmentBadge: React.FC<Props> = ({ environments = [] }) => {
</EuiBadge>
</EuiToolTip>
);
};
}

View file

@ -65,7 +65,7 @@ function getOptions(environments: string[]) {
];
}
export const EnvironmentFilter: React.FC = () => {
export function EnvironmentFilter() {
const location = useLocation();
const { uiFilters, urlParams } = useUrlParams();
@ -90,4 +90,4 @@ export const EnvironmentFilter: React.FC = () => {
isLoading={status === 'loading'}
/>
);
};
}

View file

@ -32,7 +32,7 @@ const Wrapper = styled.div<{ isSelected: boolean }>`
}
`;
const EuiTabLink = (props: Props) => {
function EuiTabLink(props: Props) {
const { isSelected, children } = props;
const className = cls('euiTab', {
@ -44,6 +44,6 @@ const EuiTabLink = (props: Props) => {
<span className={'euiTab__content'}>{children}</span>
</Wrapper>
);
};
}
export { EuiTabLink };

View file

@ -6,7 +6,12 @@
import React, { useEffect, useRef } from 'react';
export const HeightRetainer: React.FC = (props) => {
export function HeightRetainer(
props: React.DetailedHTMLProps<
React.HTMLAttributes<HTMLDivElement>,
HTMLDivElement
>
) {
const containerElement = useRef<HTMLDivElement>(null);
const minHeight = useRef<number>(0);
@ -26,4 +31,4 @@ export const HeightRetainer: React.FC = (props) => {
style={{ minHeight: minHeight.current }}
/>
);
};
}

View file

@ -112,7 +112,6 @@ export function KueryBar() {
setState({ ...state, suggestions, isLoadingSuggestions: false });
} catch (e) {
// eslint-disable-next-line no-console
console.error('Error while fetching suggestions', e);
}
}

View file

@ -14,7 +14,7 @@ interface Props {
showBetaBadge?: boolean;
}
export const LicensePrompt = ({ text, showBetaBadge = false }: Props) => {
export function LicensePrompt({ text, showBetaBadge = false }: Props) {
const licensePageUrl = useKibanaUrl(
'/app/kibana',
'/management/stack/license_management/home'
@ -60,4 +60,4 @@ export const LicensePrompt = ({ text, showBetaBadge = false }: Props) => {
);
return <>{showBetaBadge ? renderWithBetaBadge : renderLicenseBody}</>;
};
}

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import React, { ReactNode } from 'react';
import {
ERROR_GROUP_ID,
SERVICE_NAME,
@ -32,13 +32,18 @@ function getDiscoverQuery(error: APMError, kuery?: string) {
};
}
const DiscoverErrorLink: React.FC<{
function DiscoverErrorLink({
error,
kuery,
children,
}: {
children?: ReactNode;
readonly error: APMError;
readonly kuery?: string;
}> = ({ error, kuery, children }) => {
}) {
return (
<DiscoverLink query={getDiscoverQuery(error, kuery)} children={children} />
);
};
}
export { DiscoverErrorLink };

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import React, { ReactNode } from 'react';
import { SPAN_ID } from '../../../../../common/elasticsearch_fieldnames';
import { Span } from '../../../../../typings/es_schemas/ui/span';
import { DiscoverLink } from './DiscoverLink';
@ -22,8 +22,12 @@ function getDiscoverQuery(span: Span) {
};
}
export const DiscoverSpanLink: React.FC<{
export function DiscoverSpanLink({
span,
children,
}: {
readonly span: Span;
}> = ({ span, children }) => {
children?: ReactNode;
}) {
return <DiscoverLink query={getDiscoverQuery(span)} children={children} />;
};
}

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import React, { ReactNode } from 'react';
import {
PROCESSOR_EVENT,
TRACE_ID,
@ -32,10 +32,14 @@ export function getDiscoverQuery(transaction: Transaction) {
};
}
export const DiscoverTransactionLink: React.FC<{
export function DiscoverTransactionLink({
transaction,
children,
}: {
readonly transaction: Transaction;
}> = ({ transaction, children }) => {
children?: ReactNode;
}) {
return (
<DiscoverLink query={getDiscoverQuery(transaction)} children={children} />
);
};
}

View file

@ -4,24 +4,25 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import React, { ReactNode } from 'react';
import { EuiLink } from '@elastic/eui';
import { useTimeSeriesExplorerHref } from './useTimeSeriesExplorerHref';
interface Props {
children?: ReactNode;
jobId: string;
external?: boolean;
serviceName?: string;
transactionType?: string;
}
export const MLJobLink: React.FC<Props> = ({
export function MLJobLink({
jobId,
serviceName,
transactionType,
external,
children,
}) => {
}: Props) {
const href = useTimeSeriesExplorerHref({
jobId,
serviceName,
@ -36,4 +37,4 @@ export const MLJobLink: React.FC<Props> = ({
target={external ? '_blank' : undefined}
/>
);
};
}

View file

@ -11,13 +11,13 @@ interface Props extends APMLinkExtendProps {
errorGroupId: string;
}
const ErrorDetailLink = ({ serviceName, errorGroupId, ...rest }: Props) => {
function ErrorDetailLink({ serviceName, errorGroupId, ...rest }: Props) {
return (
<APMLink
path={`/services/${serviceName}/errors/${errorGroupId}`}
{...rest}
/>
);
};
}
export { ErrorDetailLink };

View file

@ -14,7 +14,7 @@ interface Props extends APMLinkExtendProps {
query?: APMQueryParams;
}
const ErrorOverviewLink = ({ serviceName, query, ...rest }: Props) => {
function ErrorOverviewLink({ serviceName, query, ...rest }: Props) {
const { urlParams } = useUrlParams();
const persistedFilters = pickKeys(
@ -35,6 +35,6 @@ const ErrorOverviewLink = ({ serviceName, query, ...rest }: Props) => {
{...rest}
/>
);
};
}
export { ErrorOverviewLink };

View file

@ -6,8 +6,8 @@
import React from 'react';
import { APMLink, APMLinkExtendProps } from './APMLink';
const HomeLink = (props: APMLinkExtendProps) => {
function HomeLink(props: APMLinkExtendProps) {
return <APMLink path="/" {...props} />;
};
}
export { HomeLink };

View file

@ -12,7 +12,7 @@ interface Props extends APMLinkExtendProps {
serviceName: string;
}
const MetricOverviewLink = ({ serviceName, ...rest }: Props) => {
function MetricOverviewLink({ serviceName, ...rest }: Props) {
const { urlParams } = useUrlParams();
const persistedFilters = pickKeys(
@ -30,6 +30,6 @@ const MetricOverviewLink = ({ serviceName, ...rest }: Props) => {
{...rest}
/>
);
};
}
export { MetricOverviewLink };

View file

@ -16,11 +16,11 @@ interface ServiceMapLinkProps extends APMLinkExtendProps {
serviceName?: string;
}
const ServiceMapLink = ({ serviceName, ...rest }: ServiceMapLinkProps) => {
function ServiceMapLink({ serviceName, ...rest }: ServiceMapLinkProps) {
const path = serviceName
? `/services/${serviceName}/service-map`
: '/service-map';
return <APMLink path={path} {...rest} />;
};
}
export { ServiceMapLink };

View file

@ -13,11 +13,11 @@ interface Props extends APMLinkExtendProps {
serviceNodeName: string;
}
const ServiceNodeMetricOverviewLink = ({
function ServiceNodeMetricOverviewLink({
serviceName,
serviceNodeName,
...rest
}: Props) => {
}: Props) {
const { urlParams } = useUrlParams();
const persistedFilters = pickKeys(
@ -37,6 +37,6 @@ const ServiceNodeMetricOverviewLink = ({
{...rest}
/>
);
};
}
export { ServiceNodeMetricOverviewLink };

View file

@ -12,7 +12,7 @@ interface Props extends APMLinkExtendProps {
serviceName: string;
}
const ServiceNodeOverviewLink = ({ serviceName, ...rest }: Props) => {
function ServiceNodeOverviewLink({ serviceName, ...rest }: Props) {
const { urlParams } = useUrlParams();
const persistedFilters = pickKeys(
@ -30,6 +30,6 @@ const ServiceNodeOverviewLink = ({ serviceName, ...rest }: Props) => {
{...rest}
/>
);
};
}
export { ServiceNodeOverviewLink };

View file

@ -14,12 +14,12 @@ import { APMLink, APMLinkExtendProps } from './APMLink';
import { useUrlParams } from '../../../../hooks/useUrlParams';
import { pickKeys } from '../../../../../common/utils/pick_keys';
const ServiceOverviewLink = (props: APMLinkExtendProps) => {
function ServiceOverviewLink(props: APMLinkExtendProps) {
const { urlParams } = useUrlParams();
const persistedFilters = pickKeys(urlParams, 'host', 'agentName');
return <APMLink path="/services" query={persistedFilters} {...props} />;
};
}
export { ServiceOverviewLink };

View file

@ -6,8 +6,8 @@
import React from 'react';
import { APMLink, APMLinkExtendProps } from './APMLink';
const SettingsLink = (props: APMLinkExtendProps) => {
function SettingsLink(props: APMLinkExtendProps) {
return <APMLink path="/settings" {...props} />;
};
}
export { SettingsLink };

View file

@ -14,7 +14,7 @@ import { APMLink, APMLinkExtendProps } from './APMLink';
import { useUrlParams } from '../../../../hooks/useUrlParams';
import { pickKeys } from '../../../../../common/utils/pick_keys';
const TraceOverviewLink = (props: APMLinkExtendProps) => {
function TraceOverviewLink(props: APMLinkExtendProps) {
const { urlParams } = useUrlParams();
const persistedFilters = pickKeys(
@ -26,6 +26,6 @@ const TraceOverviewLink = (props: APMLinkExtendProps) => {
);
return <APMLink path="/traces" query={persistedFilters} {...props} />;
};
}
export { TraceOverviewLink };

View file

@ -17,14 +17,14 @@ interface Props extends APMLinkExtendProps {
transactionType: string;
}
export const TransactionDetailLink = ({
export function TransactionDetailLink({
serviceName,
traceId,
transactionId,
transactionName,
transactionType,
...rest
}: Props) => {
}: Props) {
const { urlParams } = useUrlParams();
const persistedFilters = pickKeys(
@ -46,4 +46,4 @@ export const TransactionDetailLink = ({
{...rest}
/>
);
};
}

View file

@ -12,7 +12,7 @@ interface Props extends APMLinkExtendProps {
serviceName: string;
}
const TransactionOverviewLink = ({ serviceName, ...rest }: Props) => {
function TransactionOverviewLink({ serviceName, ...rest }: Props) {
const { urlParams } = useUrlParams();
const persistedFilters = pickKeys(
@ -31,6 +31,6 @@ const TransactionOverviewLink = ({ serviceName, ...rest }: Props) => {
{...rest}
/>
);
};
}
export { TransactionOverviewLink };

View file

@ -20,24 +20,26 @@ interface Props {
onRemove: (val: string) => void;
}
const FilterBadgeList = ({ onRemove, value }: Props) => (
<EuiFlexGrid gutterSize="s">
{value.map((val) => (
<EuiFlexItem key={val} grow={false}>
<button
type="button"
onClick={() => {
onRemove(val);
}}
>
<EuiBadge color="hollow">
<BadgeText>{val}</BadgeText>
<EuiIcon type="cross" />
</EuiBadge>
</button>
</EuiFlexItem>
))}
</EuiFlexGrid>
);
function FilterBadgeList({ onRemove, value }: Props) {
return (
<EuiFlexGrid gutterSize="s">
{value.map((val) => (
<EuiFlexItem key={val} grow={false}>
<button
type="button"
onClick={() => {
onRemove(val);
}}
>
<EuiBadge color="hollow">
<BadgeText>{val}</BadgeText>
<EuiIcon type="cross" />
</EuiBadge>
</button>
</EuiFlexItem>
))}
</EuiFlexGrid>
);
}
export { FilterBadgeList };

View file

@ -24,7 +24,7 @@ const Button = styled(EuiButtonEmpty).attrs(() => ({
type Props = React.ComponentProps<typeof Button>;
export const FilterTitleButton = (props: Props) => {
export function FilterTitleButton(props: Props) {
return (
<Button {...props}>
<EuiTitle size="xxxs" textTransform="uppercase">
@ -32,4 +32,4 @@ export const FilterTitleButton = (props: Props) => {
</EuiTitle>
</Button>
);
};
}

View file

@ -66,14 +66,7 @@ interface Props {
type Option = EuiSelectable['props']['options'][0];
const Filter = ({
name,
title,
options,
onChange,
value,
showCount,
}: Props) => {
function Filter({ name, title, options, onChange, value, showCount }: Props) {
const [showPopover, setShowPopover] = useState(false);
const toggleShowPopover = () => setShowPopover((show) => !show);
@ -176,6 +169,6 @@ const Filter = ({
) : null}
</>
);
};
}
export { Filter };

View file

@ -21,7 +21,7 @@ interface Props {
loading: boolean;
}
const ServiceNameFilter = ({ loading, serviceNames }: Props) => {
function ServiceNameFilter({ loading, serviceNames }: Props) {
const {
urlParams: { serviceName },
} = useUrlParams();
@ -72,6 +72,6 @@ const ServiceNameFilter = ({ loading, serviceNames }: Props) => {
/>
</>
);
};
}
export { ServiceNameFilter };

View file

@ -20,7 +20,7 @@ interface Props {
transactionTypes: string[];
}
const TransactionTypeFilter = ({ transactionTypes }: Props) => {
function TransactionTypeFilter({ transactionTypes }: Props) {
const {
urlParams: { transactionType },
} = useUrlParams();
@ -59,6 +59,6 @@ const TransactionTypeFilter = ({ transactionTypes }: Props) => {
/>
</>
);
};
}
export { TransactionTypeFilter };

View file

@ -31,13 +31,13 @@ const ButtonWrapper = styled.div`
display: inline-block;
`;
const LocalUIFilters = ({
function LocalUIFilters({
projection,
params,
filterNames,
children,
showCount = true,
}: Props) => {
}: Props) {
const { filters, setFilterValue, clearValues } = useLocalUIFilters({
filterNames,
projection,
@ -91,6 +91,6 @@ const LocalUIFilters = ({
) : null}
</>
);
};
}
export { LocalUIFilters };

View file

@ -91,18 +91,20 @@ export function MetadataTable({ sections }: Props) {
);
}
const NoResultFound = ({ value }: { value: string }) => (
<EuiFlexGroup justifyContent="spaceAround">
<EuiFlexItem grow={false}>
<EuiText size="s">
{i18n.translate(
'xpack.apm.propertiesTable.agentFeature.noResultFound',
{
defaultMessage: `No results for "{value}".`,
values: { value },
}
)}
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
);
function NoResultFound({ value }: { value: string }) {
return (
<EuiFlexGroup justifyContent="spaceAround">
<EuiFlexItem grow={false}>
<EuiText size="s">
{i18n.translate(
'xpack.apm.propertiesTable.agentFeature.noResultFound',
{
defaultMessage: `No results for "{value}".`,
values: { value },
}
)}
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
);
}

View file

@ -19,6 +19,7 @@ const DEFAULT_PLACEHOLDER = i18n.translate('xpack.apm.selectPlaceholder', {
* with `hasNoInitialSelection`. It uses the `placeholder` prop to populate
* the first option as the initial, not selected option.
*/
// eslint-disable-next-line react/function-component-definition
export const SelectWithPlaceholder: typeof EuiSelect = (props) => {
const placeholder = props.placeholder || DEFAULT_PLACEHOLDER;
return (

View file

@ -13,7 +13,7 @@ interface Props {
children?: React.ReactNode;
}
export const PopoverExpression = (props: Props) => {
export function PopoverExpression(props: Props) {
const { title, value, children } = props;
const [popoverOpen, setPopoverOpen] = useState(false);
@ -36,4 +36,4 @@ export const PopoverExpression = (props: Props) => {
{children}
</EuiPopover>
);
};
}

View file

@ -29,7 +29,7 @@ interface Props {
isLibraryFrame: boolean;
}
const FrameHeading: React.FC<Props> = ({ stackframe, isLibraryFrame }) => {
function FrameHeading({ stackframe, isLibraryFrame }: Props) {
const FileDetail = isLibraryFrame
? LibraryFrameFileDetail
: AppFrameFileDetail;
@ -50,6 +50,6 @@ const FrameHeading: React.FC<Props> = ({ stackframe, isLibraryFrame }) => {
)}
</FileDetails>
);
};
}
export { FrameHeading };

View file

@ -23,7 +23,7 @@ interface Props {
vars: IStackframe['vars'];
}
export const Variables = ({ vars }: Props) => {
export function Variables({ vars }: Props) {
if (!vars) {
return null;
}
@ -46,4 +46,4 @@ export const Variables = ({ vars }: Props) => {
</VariablesContainer>
</React.Fragment>
);
};
}

View file

@ -15,11 +15,7 @@ interface Props {
parentType: 'trace' | 'transaction';
}
const DurationSummaryItem = ({
duration,
totalDuration,
parentType,
}: Props) => {
function DurationSummaryItem({ duration, totalDuration, parentType }: Props) {
const calculatedTotalDuration =
totalDuration === undefined ? duration : totalDuration;
@ -41,6 +37,6 @@ const DurationSummaryItem = ({
</EuiText>
</>
);
};
}
export { DurationSummaryItem };

View file

@ -19,7 +19,7 @@ const Badge = (styled(EuiBadge)`
margin-top: ${px(units.eighth)};
` as unknown) as typeof EuiBadge;
export const ErrorCountSummaryItemBadge = ({ count }: Props) => {
export function ErrorCountSummaryItemBadge({ count }: Props) {
const theme = useTheme();
return (
@ -31,4 +31,4 @@ export const ErrorCountSummaryItemBadge = ({ count }: Props) => {
})}
</Badge>
);
};
}

View file

@ -20,7 +20,7 @@ interface Props {
errorCount: number;
}
const getTransactionResultSummaryItem = (transaction: Transaction) => {
function getTransactionResultSummaryItem(transaction: Transaction) {
const result = transaction.transaction.result;
const isRumAgent = isRumAgentName(transaction.agent.name);
const url = isRumAgent
@ -39,13 +39,9 @@ const getTransactionResultSummaryItem = (transaction: Transaction) => {
}
return null;
};
}
const TransactionSummary = ({
transaction,
totalDuration,
errorCount,
}: Props) => {
function TransactionSummary({ transaction, totalDuration, errorCount }: Props) {
const items = [
<TimestampTooltip time={transaction.timestamp.us / 1000} />,
<DurationSummaryItem
@ -61,6 +57,6 @@ const TransactionSummary = ({
];
return <Summary items={items} />;
};
}
export { TransactionSummary };

View file

@ -26,7 +26,7 @@ const Item = styled(EuiFlexItem)`
}
`;
const Summary = ({ items }: Props) => {
function Summary({ items }: Props) {
const filteredItems = items.filter(Boolean) as React.ReactElement[];
return (
@ -38,6 +38,6 @@ const Summary = ({ items }: Props) => {
))}
</EuiFlexGrid>
);
};
}
export { Summary };

View file

@ -24,7 +24,7 @@ const ScrollableContainer = styled.div`
overflow: scroll;
`;
export const CustomLinkPopover = ({
export function CustomLinkPopover({
customLinks,
onCreateCustomLinkClick,
onClose,
@ -34,7 +34,7 @@ export const CustomLinkPopover = ({
onCreateCustomLinkClick: () => void;
onClose: () => void;
transaction: Transaction;
}) => {
}) {
return (
<>
<EuiPopoverTitle>
@ -71,4 +71,4 @@ export const CustomLinkPopover = ({
</ScrollableContainer>
</>
);
};
}

View file

@ -24,28 +24,30 @@ const TruncateText = styled(EuiText)`
${truncate(px(units.unit * 25))}
`;
export const CustomLinkSection = ({
export function CustomLinkSection({
customLinks,
transaction,
}: {
customLinks: CustomLink[];
transaction: Transaction;
}) => (
<ul>
{customLinks.map((link) => {
let href = link.url;
try {
href = Mustache.render(link.url, transaction);
} catch (e) {
// ignores any error that happens
}
return (
<LinkContainer key={link.id}>
<EuiLink href={href} target="_blank">
<TruncateText size="s">{link.label}</TruncateText>
</EuiLink>
</LinkContainer>
);
})}
</ul>
);
}) {
return (
<ul>
{customLinks.map((link) => {
let href = link.url;
try {
href = Mustache.render(link.url, transaction);
} catch (e) {
// ignores any error that happens
}
return (
<LinkContainer key={link.id}>
<EuiLink href={href} target="_blank">
<TruncateText size="s">{link.label}</TruncateText>
</EuiLink>
</LinkContainer>
);
})}
</ul>
);
}

View file

@ -14,46 +14,48 @@ import {
import { i18n } from '@kbn/i18n';
import { APMLink } from '../../Links/apm/APMLink';
export const ManageCustomLink = ({
export function ManageCustomLink({
onCreateCustomLinkClick,
showCreateCustomLinkButton = true,
}: {
onCreateCustomLinkClick: () => void;
showCreateCustomLinkButton?: boolean;
}) => (
<EuiFlexGroup>
<EuiFlexItem>
<EuiFlexGroup justifyContent="flexEnd" gutterSize="none">
<EuiFlexItem grow={false} style={{ justifyContent: 'center' }}>
<EuiToolTip
position="top"
content={i18n.translate('xpack.apm.customLink.buttom.manage', {
defaultMessage: 'Manage custom links',
})}
>
<APMLink path={`/settings/customize-ui`}>
<EuiIcon
type="gear"
color="text"
aria-label="Custom links settings page"
/>
</APMLink>
</EuiToolTip>
</EuiFlexItem>
{showCreateCustomLinkButton && (
<EuiFlexItem grow={false}>
<EuiButtonEmpty
iconType="plusInCircle"
size="xs"
onClick={onCreateCustomLinkClick}
>
{i18n.translate('xpack.apm.customLink.buttom.create.title', {
defaultMessage: 'Create',
}) {
return (
<EuiFlexGroup>
<EuiFlexItem>
<EuiFlexGroup justifyContent="flexEnd" gutterSize="none">
<EuiFlexItem grow={false} style={{ justifyContent: 'center' }}>
<EuiToolTip
position="top"
content={i18n.translate('xpack.apm.customLink.buttom.manage', {
defaultMessage: 'Manage custom links',
})}
</EuiButtonEmpty>
>
<APMLink path={`/settings/customize-ui`}>
<EuiIcon
type="gear"
color="text"
aria-label="Custom links settings page"
/>
</APMLink>
</EuiToolTip>
</EuiFlexItem>
)}
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
);
{showCreateCustomLinkButton && (
<EuiFlexItem grow={false}>
<EuiButtonEmpty
iconType="plusInCircle"
size="xs"
onClick={onCreateCustomLinkClick}
>
{i18n.translate('xpack.apm.customLink.buttom.create.title', {
defaultMessage: 'Create',
})}
</EuiButtonEmpty>
</EuiFlexItem>
)}
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
);
}

View file

@ -37,7 +37,7 @@ const SeeMoreButton = styled.button<{ show: boolean }>`
}
`;
export const CustomLink = ({
export function CustomLink({
customLinks,
status,
onCreateCustomLinkClick,
@ -49,7 +49,7 @@ export const CustomLink = ({
onCreateCustomLinkClick: () => void;
onSeeMoreClick: () => void;
transaction: Transaction;
}) => {
}) {
const renderEmptyPrompt = (
<>
<EuiText size="xs" grow={false} style={{ width: px(300) }}>
@ -125,4 +125,4 @@ export const CustomLink = ({
)}
</>
);
};
}

View file

@ -6,10 +6,8 @@
import { EuiButtonEmpty } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { FunctionComponent, useMemo, useState, MouseEvent } from 'react';
import React, { MouseEvent, useMemo, useState } from 'react';
import url from 'url';
import { Filter } from '../../../../common/custom_link/custom_link_types';
import { Transaction } from '../../../../typings/es_schemas/ui/transaction';
import {
ActionMenu,
ActionMenuDivider,
@ -19,32 +17,34 @@ import {
SectionSubtitle,
SectionTitle,
} from '../../../../../observability/public';
import { Filter } from '../../../../common/custom_link/custom_link_types';
import { Transaction } from '../../../../typings/es_schemas/ui/transaction';
import { useApmPluginContext } from '../../../hooks/useApmPluginContext';
import { useFetcher } from '../../../hooks/useFetcher';
import { useLicense } from '../../../hooks/useLicense';
import { useLocation } from '../../../hooks/useLocation';
import { useUrlParams } from '../../../hooks/useUrlParams';
import { CustomLinkFlyout } from '../../app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout';
import { convertFiltersToQuery } from '../../app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/helper';
import { CustomLink } from './CustomLink';
import { CustomLinkPopover } from './CustomLink/CustomLinkPopover';
import { getSections } from './sections';
import { useLicense } from '../../../hooks/useLicense';
import { convertFiltersToQuery } from '../../app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/helper';
interface Props {
readonly transaction: Transaction;
}
const ActionMenuButton = ({ onClick }: { onClick: () => void }) => (
<EuiButtonEmpty iconType="arrowDown" iconSide="right" onClick={onClick}>
{i18n.translate('xpack.apm.transactionActionMenu.actionsButtonLabel', {
defaultMessage: 'Actions',
})}
</EuiButtonEmpty>
);
function ActionMenuButton({ onClick }: { onClick: () => void }) {
return (
<EuiButtonEmpty iconType="arrowDown" iconSide="right" onClick={onClick}>
{i18n.translate('xpack.apm.transactionActionMenu.actionsButtonLabel', {
defaultMessage: 'Actions',
})}
</EuiButtonEmpty>
);
}
export const TransactionActionMenu: FunctionComponent<Props> = ({
transaction,
}: Props) => {
export function TransactionActionMenu({ transaction }: Props) {
const license = useLicense();
const hasValidLicense = license?.isActive && license?.hasAtLeast('gold');
@ -211,4 +211,4 @@ export const TransactionActionMenu: FunctionComponent<Props> = ({
</ActionMenu>
</>
);
};
}

View file

@ -29,7 +29,7 @@ const formatTooltipValue = (coordinate: Coordinate) => {
: NOT_AVAILABLE_LABEL;
};
const TransactionBreakdownGraph: React.FC<Props> = (props) => {
function TransactionBreakdownGraph(props: Props) {
const { timeseries } = props;
const trackApmEvent = useUiTracker({ app: 'apm' });
const handleHover = useMemo(
@ -49,6 +49,6 @@ const TransactionBreakdownGraph: React.FC<Props> = (props) => {
onHover={handleHover}
/>
);
};
}
export { TransactionBreakdownGraph };

View file

@ -31,10 +31,7 @@ const Description = styled.span`
}
`;
const KpiDescription: React.FC<{
name: string;
color: string;
}> = ({ name, color }) => {
function KpiDescription({ name, color }: { name: string; color: string }) {
return (
<EuiFlexGroup
alignItems="center"
@ -52,9 +49,9 @@ const KpiDescription: React.FC<{
</EuiFlexItem>
</EuiFlexGroup>
);
};
}
const TransactionBreakdownKpiList: React.FC<Props> = ({ kpis }) => {
function TransactionBreakdownKpiList({ kpis }: Props) {
return (
<EuiFlexGrid>
{kpis.map((kpi) => (
@ -73,6 +70,6 @@ const TransactionBreakdownKpiList: React.FC<Props> = ({ kpis }) => {
))}
</EuiFlexGrid>
);
};
}
export { TransactionBreakdownKpiList };

View file

@ -21,7 +21,7 @@ const emptyMessage = i18n.translate('xpack.apm.transactionBreakdown.noData', {
defaultMessage: 'No data within this time range.',
});
const TransactionBreakdown = () => {
function TransactionBreakdown() {
const { data, status } = useTransactionBreakdown();
const { kpis, timeseries } = data;
const noHits = data.kpis.length === 0 && status === FETCH_STATUS.SUCCESS;
@ -51,6 +51,6 @@ const TransactionBreakdown = () => {
</EuiFlexGroup>
</EuiPanel>
);
};
}
export { TransactionBreakdown };

View file

@ -26,7 +26,7 @@ interface Props {
overlay: Maybe<HTMLElement>;
}
export const AnnotationsPlot = ({ plotValues, annotations }: Props) => {
export function AnnotationsPlot({ plotValues, annotations }: Props) {
const theme = useTheme();
const tickValues = annotations.map((annotation) => annotation['@timestamp']);
@ -70,4 +70,4 @@ export const AnnotationsPlot = ({ plotValues, annotations }: Props) => {
))}
</>
);
};
}

View file

@ -21,7 +21,7 @@ const tickFormatY = (y?: number) => {
return asPercent(y || 0, 1);
};
export const ErroneousTransactionsRateChart = () => {
export function ErroneousTransactionsRateChart() {
const { urlParams, uiFilters } = useUrlParams();
const syncedChartsProps = useChartsSync();
@ -105,4 +105,4 @@ export const ErroneousTransactionsRateChart = () => {
/>
</EuiPanel>
);
};
}

Some files were not shown because too many files have changed in this diff Show more