Skip to content

Commit

Permalink
Merge branch 'main' into fix-osquery-no-response-actions
Browse files Browse the repository at this point in the history
  • Loading branch information
szwarckonrad authored Sep 26, 2023
2 parents 89f3c2b + a8de9eb commit 203c9db
Show file tree
Hide file tree
Showing 39 changed files with 1,253 additions and 37 deletions.
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,7 @@ packages/kbn-management/cards_navigation @elastic/platform-deployment-management
src/plugins/management @elastic/platform-deployment-management
packages/kbn-management/settings/components/field_input @elastic/platform-deployment-management
packages/kbn-management/settings/components/field_row @elastic/platform-deployment-management
packages/kbn-management/settings/components/form @elastic/platform-deployment-management
packages/kbn-management/settings/field_definition @elastic/platform-deployment-management
packages/kbn-management/settings/setting_ids @elastic/appex-sharedux @elastic/platform-deployment-management
packages/kbn-management/settings/section_registry @elastic/appex-sharedux @elastic/platform-deployment-management
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,7 @@
"@kbn/management-plugin": "link:src/plugins/management",
"@kbn/management-settings-components-field-input": "link:packages/kbn-management/settings/components/field_input",
"@kbn/management-settings-components-field-row": "link:packages/kbn-management/settings/components/field_row",
"@kbn/management-settings-components-form": "link:packages/kbn-management/settings/components/form",
"@kbn/management-settings-field-definition": "link:packages/kbn-management/settings/field_definition",
"@kbn/management-settings-ids": "link:packages/kbn-management/settings/setting_ids",
"@kbn/management-settings-section-registry": "link:packages/kbn-management/settings/section_registry",
Expand Down Expand Up @@ -867,7 +868,6 @@
"d3": "3.5.17",
"d3-array": "2.12.1",
"d3-brush": "^3.0.0",
"d3-cloud": "1.2.5",
"d3-interpolate": "^3.0.1",
"d3-scale": "^3.3.0",
"d3-selection": "^3.0.0",
Expand Down
18 changes: 18 additions & 0 deletions packages/kbn-management/settings/components/form/README.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
id: management/settings/components/form
slug: /management/settings/components/form
title: Management Settings Form Component
description: A package containing a component for rendering the form in the Advanced Settings UI.
tags: ['management', 'settings']
date: 2023-09-12
---

## Description

This package contains a component for rendering the Advanced Settings UI form that contains `FieldRow` components, each of which displays a single UiSetting field row.
The form also handles the logic for saving any changes to the UiSettings values by directly communicating with the uiSettings service.


## Notes

- This implementation was extracted from the `Form` component in the `advancedSettings` plugin.
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import React from 'react';
import { fireEvent, render } from '@testing-library/react';
import {
BottomBar,
BottomBarProps,
DATA_TEST_SUBJ_SAVE_BUTTON,
DATA_TEST_SUBJ_CANCEL_BUTTON,
} from './bottom_bar';
import { wrap } from '../mocks';

const saveAll = jest.fn();
const clearAllUnsaved = jest.fn();
const unsavedChangesCount = 3;

const defaultProps: BottomBarProps = {
onSaveAll: saveAll,
onClearAllUnsaved: clearAllUnsaved,
hasInvalidChanges: false,
unsavedChangesCount,
isLoading: false,
};

const unsavedChangesCountText = unsavedChangesCount + ' unsaved settings';

describe('BottomBar', () => {
it('renders without errors', () => {
const { container } = render(wrap(<BottomBar {...defaultProps} />));
expect(container).toBeInTheDocument();
});

it('fires saveAll when the Save button is clicked', () => {
const { getByTestId } = render(wrap(<BottomBar {...defaultProps} />));

const input = getByTestId(DATA_TEST_SUBJ_SAVE_BUTTON);
fireEvent.click(input);
expect(saveAll).toHaveBeenCalled();
});

it('fires clearAllUnsaved when the Cancel button is clicked', () => {
const { getByTestId } = render(wrap(<BottomBar {...defaultProps} />));

const input = getByTestId(DATA_TEST_SUBJ_CANCEL_BUTTON);
fireEvent.click(input);
expect(saveAll).toHaveBeenCalled();
});

it('renders unsaved changes count', () => {
const { getByText } = render(wrap(<BottomBar {...defaultProps} />));

expect(getByText(unsavedChangesCountText)).toBeInTheDocument();
});

it('save button is disabled when there are invalid changes', () => {
const { getByTestId } = render(
wrap(<BottomBar {...{ ...defaultProps, hasInvalidChanges: true }} />)
);

const input = getByTestId(DATA_TEST_SUBJ_SAVE_BUTTON);
expect(input).toBeDisabled();
});

it('save button is loading when in loading state', () => {
const { getByTestId, getByLabelText } = render(
wrap(<BottomBar {...{ ...defaultProps, isLoading: true }} />)
);

const input = getByTestId(DATA_TEST_SUBJ_SAVE_BUTTON);
expect(input).toBeDisabled();
expect(getByLabelText('Loading')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import React from 'react';

import {
EuiBottomBar,
EuiButton,
EuiButtonEmpty,
EuiFlexGroup,
EuiFlexItem,
EuiToolTip,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { UnsavedCount } from './unsaved_count';
import { useFormStyles } from '../form.styles';

export const DATA_TEST_SUBJ_SAVE_BUTTON = 'settings-save-button';
export const DATA_TEST_SUBJ_CANCEL_BUTTON = 'settings-cancel-button';

/**
* Props for a {@link BottomBar} component.
*/
export interface BottomBarProps {
onSaveAll: () => void;
onClearAllUnsaved: () => void;
hasInvalidChanges: boolean;
isLoading: boolean;
unsavedChangesCount: number;
}

/**
* Component for displaying the bottom bar of a {@link Form}.
*/
export const BottomBar = ({
onSaveAll,
onClearAllUnsaved,
hasInvalidChanges,
isLoading,
unsavedChangesCount,
}: BottomBarProps) => {
const { cssFormButton, cssFormUnsavedCount } = useFormStyles();

return (
<EuiBottomBar>
<EuiFlexGroup
justifyContent="spaceBetween"
alignItems="center"
responsive={false}
gutterSize="s"
>
<EuiFlexItem grow={false} css={cssFormUnsavedCount}>
<UnsavedCount unsavedCount={unsavedChangesCount} />
</EuiFlexItem>
<EuiFlexItem />
<EuiFlexItem grow={false}>
<EuiButtonEmpty
css={cssFormButton}
color="ghost"
size="s"
iconType="cross"
onClick={onClearAllUnsaved}
data-test-subj={DATA_TEST_SUBJ_CANCEL_BUTTON}
>
{i18n.translate('management.settings.form.cancelButtonLabel', {
defaultMessage: 'Cancel changes',
})}
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiToolTip
content={
hasInvalidChanges &&
i18n.translate('management.settings.form.saveButtonTooltipWithInvalidChanges', {
defaultMessage: 'Fix invalid settings before saving.',
})
}
>
<EuiButton
css={cssFormButton}
disabled={hasInvalidChanges}
color="success"
fill
size="s"
iconType="check"
onClick={onSaveAll}
isLoading={isLoading}
data-test-subj={DATA_TEST_SUBJ_SAVE_BUTTON}
>
{i18n.translate('management.settings.form.saveButtonLabel', {
defaultMessage: 'Save changes',
})}
</EuiButton>
</EuiToolTip>
</EuiFlexItem>
</EuiFlexGroup>
</EuiBottomBar>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

export { BottomBar } from './bottom_bar';
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import React from 'react';

import { EuiText } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { useFormStyles } from '../form.styles';

/**
* Props for a {@link UnsavedCount} component.
*/
interface UnsavedCountProps {
unsavedCount: number;
}

/**
* Component for displaying the count of unsaved changes in a {@link BottomBar}.
*/
export const UnsavedCount = ({ unsavedCount }: UnsavedCountProps) => {
const { cssFormUnsavedCountMessage } = useFormStyles();
return (
<EuiText size="s" color="ghost" css={cssFormUnsavedCountMessage}>
<FormattedMessage
id="management.settings.form.countOfSettingsChanged"
defaultMessage="{unsavedCount} unsaved {unsavedCount, plural,
one {setting}
other {settings}
}"
values={{
unsavedCount,
}}
/>
</EuiText>
);
};
33 changes: 33 additions & 0 deletions packages/kbn-management/settings/components/form/form.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { useEuiTheme, euiBreakpoint } from '@elastic/eui';
import { css } from '@emotion/react';

/**
* A React hook that provides stateful `css` classes for the {@link Form} component.
*/
export const useFormStyles = () => {
const euiTheme = useEuiTheme();
const { size, colors } = euiTheme.euiTheme;

return {
cssFormButton: css`
width: 100%;
`,
cssFormUnsavedCount: css`
${euiBreakpoint(euiTheme, ['xs'])} {
display: none;
}
`,
cssFormUnsavedCountMessage: css`
box-shadow: -${size.xs} 0 ${colors.warning};
padding-left: ${size.s};
`,
};
};
Loading

0 comments on commit 203c9db

Please sign in to comment.