Skip to content

Commit

Permalink
Merge branch 'main' into renovate/main-jquery
Browse files Browse the repository at this point in the history
  • Loading branch information
nickofthyme authored Jan 31, 2025
2 parents e528334 + 50b8776 commit 3f968a2
Show file tree
Hide file tree
Showing 22 changed files with 258 additions and 92 deletions.
3 changes: 3 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -1589,6 +1589,9 @@ packages/kbn-monaco/src/esql @elastic/kibana-esql
/.eslintignore @elastic/kibana-operations

# QA - Appex QA
/packages/kbn-es/src/serverless_resources/project_roles/es/roles.yml @elastic/appex-qa
/packages/kbn-es/src/serverless_resources/project_roles/oblt/roles.yml @elastic/appex-qa
/packages/kbn-es/src/serverless_resources/project_roles/security/roles.yml @elastic/appex-qa
/x-pack/platform/plugins/shared/maps/ui_tests @elastic/appex-qa # temporarily
/x-pack/platform/plugins/private/discover_enhanced/ui_tests/ @elastic/appex-qa # temporarily
/x-pack/test/functional/fixtures/package_registry_config.yml @elastic/appex-qa # No usages found
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,13 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { EuiScreenReaderOnly } from '@elastic/eui';
import { ViewMode } from '@kbn/presentation-publishing';
import { i18n } from '@kbn/i18n';
import classNames from 'classnames';
import React, { useCallback } from 'react';
import { placeholderTitle } from './presentation_panel_title';
import { DefaultPresentationPanelApi, PresentationPanelInternalProps } from '../types';
import { PresentationPanelTitle } from './presentation_panel_title';
import { usePresentationPanelHeaderActions } from './use_presentation_panel_header_actions';

const getAriaLabelForTitle = (title?: string) => {
return title
? i18n.translate('presentationPanel.enhancedAriaLabel', {
defaultMessage: 'Panel: {title}',
values: { title: title || placeholderTitle },
})
: i18n.translate('presentationPanel.ariaLabel', {
defaultMessage: 'Panel',
});
};

export type PresentationPanelHeaderProps<ApiType extends DefaultPresentationPanelApi> = {
api: ApiType;
headerId: string;
Expand Down Expand Up @@ -72,13 +58,6 @@ export const PresentationPanelHeader = <

if (!showPanelBar) return null;

const ariaLabel = getAriaLabelForTitle(showPanelBar ? panelTitle : undefined);
const ariaLabelElement = (
<EuiScreenReaderOnly>
<span id={headerId}>{ariaLabel}</span>
</EuiScreenReaderOnly>
);

const headerClasses = classNames('embPanel__header', {
'embPanel--dragHandle': viewMode === 'edit',
'embPanel__header--floater': !showPanelBar,
Expand All @@ -93,17 +72,21 @@ export const PresentationPanelHeader = <
className={headerClasses}
data-test-subj={`embeddablePanelHeading-${(panelTitle || '').replace(/\s/g, '')}`}
>
<h2 ref={memoizedSetDragHandle} data-test-subj="dashboardPanelTitle" className={titleClasses}>
{ariaLabelElement}
<div
ref={memoizedSetDragHandle}
data-test-subj="dashboardPanelTitle"
className={titleClasses}
>
<PresentationPanelTitle
api={api}
headerId={headerId}
viewMode={viewMode}
hideTitle={hideTitle}
panelTitle={panelTitle}
panelDescription={panelDescription}
/>
{showBadges && badgeElements}
</h2>
</div>
{showNotifications && notificationElements}
</figcaption>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { EuiIcon, EuiLink, EuiToolTip } from '@elastic/eui';
import { EuiIcon, EuiLink, EuiScreenReaderOnly, EuiToolTip } from '@elastic/eui';
import classNames from 'classnames';
import { once } from 'lodash';
import React, { useMemo, useEffect, useRef, useState } from 'react';
Expand Down Expand Up @@ -36,6 +36,17 @@ export const placeholderTitle = i18n.translate('presentationPanel.placeholderTit
defaultMessage: '[No Title]',
});

const getAriaLabelForTitle = (title?: string) => {
return title
? i18n.translate('presentationPanel.enhancedAriaLabel', {
defaultMessage: 'Panel: {title}',
values: { title: title || placeholderTitle },
})
: i18n.translate('presentationPanel.ariaLabel', {
defaultMessage: 'Panel',
});
};

const createDocumentMouseMoveListener = once(() => fromEvent<MouseEvent>(document, 'mousemove'));
const createDocumentMouseUpListener = once(() => fromEvent<MouseEvent>(document, 'mouseup'));

Expand Down Expand Up @@ -83,18 +94,21 @@ export const usePresentationPanelTitleClickHandler = (titleElmRef: HTMLElement |

export const PresentationPanelTitle = ({
api,
headerId,
viewMode,
hideTitle,
panelTitle,
panelDescription,
}: {
api: unknown;
headerId: string;
hideTitle?: boolean;
panelTitle?: string;
panelDescription?: string;
viewMode?: ViewMode;
}) => {
const [panelTitleElmRef, setPanelTitleElmRef] = useState<HTMLElement | null>(null);

const panelTitleElement = useMemo(() => {
if (hideTitle) return null;
const titleClassNames = classNames('embPanel__titleText', {
Expand Down Expand Up @@ -146,6 +160,14 @@ export const PresentationPanelTitle = ({
</span>
);
}

const ariaLabel = getAriaLabelForTitle(panelTitle);
const ariaLabelElement = (
<EuiScreenReaderOnly>
<span id={headerId}>{ariaLabel}</span>
</EuiScreenReaderOnly>
);

return (
<EuiToolTip
title={!hideTitle ? panelTitle || undefined : undefined}
Expand All @@ -155,17 +177,23 @@ export const PresentationPanelTitle = ({
anchorClassName="embPanel__titleTooltipAnchor"
anchorProps={{ 'data-test-subj': 'embeddablePanelTooltipAnchor' }}
>
<span data-test-subj="embeddablePanelTitleInner" className="embPanel__titleInner">
{!hideTitle ? <>{panelTitleElement}&nbsp;</> : null}
<div data-test-subj="embeddablePanelTitleInner" className="embPanel__titleInner">
{!hideTitle ? (
<h2>
{ariaLabelElement}
{panelTitleElement}&nbsp;
</h2>
) : null}
<EuiIcon
type="iInCircle"
color="subdued"
data-test-subj="embeddablePanelTitleDescriptionIcon"
tabIndex={0}
/>
</span>
</div>
</EuiToolTip>
);
}, [hideTitle, panelDescription, panelTitle, panelTitleElement]);
}, [hideTitle, panelDescription, panelTitle, panelTitleElement, headerId]);

return describedPanelTitleElement;
};
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@ export const getCaseRoute = () =>
includeComments: false,
});

const { comments, ...caseWithoutComments } = res;

return response.ok({
body: res,
body: caseWithoutComments,
});
} catch (error) {
throw createCaseError({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { createFleetTestRendererMock } from '../../../../../mock';

import {
useUIExtension,
sendGetAgentStatus,
sendGetOneAgentPolicy,
sendGetOnePackagePolicy,
sendUpgradePackagePolicyDryRun,
Expand All @@ -24,6 +23,7 @@ import {
useGetAgentPolicies,
useMultipleAgentPolicies,
useGetPackagePolicies,
sendBulkGetAgentPoliciesForRq,
} from '../../../hooks';
import { useGetOnePackagePolicy } from '../../../../integrations/hooks';

Expand All @@ -44,6 +44,7 @@ jest.mock('../../../hooks', () => {
return {
...jest.requireActual('../../../hooks'),
sendGetAgentStatus: jest.fn(),
sendBulkGetAgentPoliciesForRq: jest.fn(),
sendUpdatePackagePolicy: jest.fn(),
sendGetOnePackagePolicy: jest.fn(),
sendGetOneAgentPolicy: jest.fn(),
Expand Down Expand Up @@ -261,7 +262,7 @@ describe('edit package policy page', () => {
(sendUpdatePackagePolicy as MockFn).mockResolvedValue({});
(useStartServices().application.navigateToUrl as MockFn).mockReset();
(useStartServices().notifications.toasts.addError as MockFn).mockReset();
(sendGetAgentStatus as MockFn).mockResolvedValue({ data: { results: { total: 0 } } });
(sendBulkGetAgentPoliciesForRq as MockFn).mockResolvedValue({ data: [] });
(sendBulkGetAgentPolicies as MockFn).mockResolvedValue({
data: { items: [{ id: 'agent-policy-1', name: 'Agent policy 1' }] },
});
Expand Down Expand Up @@ -477,7 +478,7 @@ describe('edit package policy page', () => {
});

it('should not show confirmation modal if package is on agentless policy', async () => {
(sendGetAgentStatus as MockFn).mockResolvedValue({ data: { results: { total: 1 } } });
(sendBulkGetAgentPoliciesForRq as MockFn).mockResolvedValue({ data: [{ agents: 1 }] });
(useGetOnePackagePolicy as MockFn).mockReturnValue({
data: {
item: mockPackagePolicyAgentless,
Expand Down Expand Up @@ -529,8 +530,8 @@ describe('edit package policy page', () => {
beforeEach(() => {
useMultipleAgentPoliciesMock.mockReturnValue({ canUseMultipleAgentPolicies: true });

(sendGetAgentStatus as jest.MockedFunction<any>).mockResolvedValue({
data: { results: { total: 0 } },
(sendBulkGetAgentPoliciesForRq as jest.MockedFunction<any>).mockResolvedValue({
data: [],
});
jest.clearAllMocks();
});
Expand Down Expand Up @@ -569,7 +570,6 @@ describe('edit package policy page', () => {
policy_ids: ['agent-policy-1', 'agent-policy-2'],
})
);
expect(sendGetAgentStatus).toHaveBeenCalledTimes(1);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ import {
useStartServices,
useConfig,
useUIExtension,
sendGetAgentStatus,
useAuthz,
sendBulkGetAgentPoliciesForRq,
} from '../../../hooks';
import {
useBreadcrumbs as useIntegrationsBreadcrumbs,
Expand Down Expand Up @@ -159,24 +159,74 @@ export const EditPackagePolicyForm = memo<{

const [hasAgentPolicyError, setHasAgentPolicyError] = useState<boolean>(false);

const agentPoliciesToAdd = useMemo(
() => [
...agentPolicies
.filter(
(policy) =>
!existingAgentPolicies.find((existingPolicy) => existingPolicy.id === policy.id)
)
.map((policy) => policy.name),
...(newAgentPolicyName ? [newAgentPolicyName] : []),
],
[agentPolicies, existingAgentPolicies, newAgentPolicyName]
);
const agentPoliciesToRemove = useMemo(
() =>
existingAgentPolicies.filter(
(existingPolicy) => !agentPolicies.find((policy) => policy.id === existingPolicy.id)
),
[agentPolicies, existingAgentPolicies]
);

const agentPoliciesToRemoveIds = useMemo(
() => agentPoliciesToRemove.map((policy) => policy.id),
[agentPoliciesToRemove]
);

const agentPoliciesToRemoveName = useMemo(
() => agentPoliciesToRemove.map((policy) => policy.name),
[agentPoliciesToRemove]
);

// Retrieve agent count
const [agentCount, setAgentCount] = useState<number>(0);
const [impactedAgentCount, setImpactedAgentCount] = useState<number>(0);
useEffect(() => {
const getAgentCount = async () => {
let count = 0;
for (const id of packagePolicy.policy_ids) {
const { data } = await sendGetAgentStatus({ policyId: id });
if (data?.results.active) {
count += data.results.active;
const policiesToFetchIds = [...packagePolicy.policy_ids, ...agentPoliciesToRemoveIds];
try {
const bulkGetAgentPoliciesResponse = await sendBulkGetAgentPoliciesForRq(
policiesToFetchIds,
{
ignoreMissing: true,
}
);

let count = 0;
let impactedCount = 0;
for (const item of bulkGetAgentPoliciesResponse.items) {
if (packagePolicy.policy_ids.includes(item.id)) {
count += item.agents ?? 0;
}

impactedCount += item.agents ?? 0;
}
setAgentCount(count);
setImpactedAgentCount(impactedCount);
} catch (err) {
setAgentCount(0);
setImpactedAgentCount(0);
}
setAgentCount(count);
};

if (isFleetEnabled && packagePolicy.policy_ids.length > 0) {
if (
isFleetEnabled &&
(packagePolicy.policy_ids.length > 0 || agentPoliciesToRemoveIds.length > 0)
) {
getAgentCount();
}
}, [packagePolicy.policy_ids, isFleetEnabled]);
}, [packagePolicy.policy_ids, agentPoliciesToRemoveIds, isFleetEnabled]);

const handleExtensionViewOnChange = useCallback<
PackagePolicyEditExtensionComponentProps['onChange']
Expand Down Expand Up @@ -225,28 +275,6 @@ export const EditPackagePolicyForm = memo<{
}
}, [existingAgentPolicies, isFirstLoad]);

const agentPoliciesToAdd = useMemo(
() => [
...agentPolicies
.filter(
(policy) =>
!existingAgentPolicies.find((existingPolicy) => existingPolicy.id === policy.id)
)
.map((policy) => policy.name),
...(newAgentPolicyName ? [newAgentPolicyName] : []),
],
[agentPolicies, existingAgentPolicies, newAgentPolicyName]
);
const agentPoliciesToRemove = useMemo(
() =>
existingAgentPolicies
.filter(
(existingPolicy) => !agentPolicies.find((policy) => policy.id === existingPolicy.id)
)
.map((policy) => policy.name),
[agentPolicies, existingAgentPolicies]
);

const onSubmit = async () => {
if (formState === 'VALID' && hasErrors) {
setFormState('INVALID');
Expand Down Expand Up @@ -518,12 +546,12 @@ export const EditPackagePolicyForm = memo<{
/>
{formState === 'CONFIRM' && (
<ConfirmDeployAgentPolicyModal
agentCount={agentCount}
agentCount={impactedAgentCount}
agentPolicies={agentPolicies}
onConfirm={onSubmit}
onCancel={() => setFormState('VALID')}
agentPoliciesToAdd={agentPoliciesToAdd}
agentPoliciesToRemove={agentPoliciesToRemove}
agentPoliciesToRemove={agentPoliciesToRemoveName}
/>
)}
{packageInfo && isRootPrivilegesRequired(packageInfo) ? (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,18 @@ export const sendBulkGetAgentPolicies = (
});
};

export const sendBulkGetAgentPoliciesForRq = (
ids: string[],
options?: { full?: boolean; ignoreMissing?: boolean }
) => {
return sendRequestForRq<BulkGetAgentPoliciesResponse>({
path: agentPolicyRouteService.getBulkGetPath(),
method: 'post',
body: JSON.stringify({ ids, full: options?.full, ignoreMissing: options?.ignoreMissing }),
version: API_VERSIONS.public.v1,
});
};

export const sendGetAgentPolicies = (query?: GetAgentPoliciesRequest['query']) => {
return sendRequest<GetAgentPoliciesResponse>({
path: agentPolicyRouteService.getListPath(),
Expand Down
Loading

0 comments on commit 3f968a2

Please sign in to comment.