Skip to content

Commit

Permalink
[Discover] Show "unsaved changes" label when in unsaved state of save…
Browse files Browse the repository at this point in the history
…d search (elastic#169548)

- Resolves elastic#135887

## Summary

This PR adds "Unsaved changes" badge to Discover for modified saved
searches. It also removes "Reset search" button from the histogram area.
Code for the badge is added to a new package
`@kbn/unsaved-changes-badge`.

<img width="600" alt="Screenshot 2023-10-23 at 18 05 34"
src="https://github.com/elastic/kibana/assets/1415710/ad200a28-79e1-4cc5-8e28-6352d4b85322">

![Oct-23-2023
18-06-39](https://github.com/elastic/kibana/assets/1415710/cacf4ff2-525c-4759-aba9-34ce75089ddd)


### Checklist

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [x] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
2 people authored and jillguyonnet committed Nov 16, 2023
1 parent 888c7ea commit fbfcf39
Show file tree
Hide file tree
Showing 52 changed files with 1,189 additions and 143 deletions.
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Expand Up @@ -789,6 +789,7 @@ packages/kbn-unified-field-list @elastic/kibana-data-discovery
examples/unified_field_list_examples @elastic/kibana-data-discovery
src/plugins/unified_histogram @elastic/kibana-data-discovery
src/plugins/unified_search @elastic/kibana-visualizations
packages/kbn-unsaved-changes-badge @elastic/kibana-data-discovery
x-pack/plugins/upgrade_assistant @elastic/platform-deployment-management
x-pack/plugins/uptime @elastic/obs-ux-infra_services-team
x-pack/plugins/drilldowns/url_drilldown @elastic/appex-sharedux
Expand Down
3 changes: 2 additions & 1 deletion .i18nrc.json
Expand Up @@ -132,7 +132,8 @@
"unifiedSearch": "src/plugins/unified_search",
"unifiedFieldList": "packages/kbn-unified-field-list",
"unifiedHistogram": "src/plugins/unified_histogram",
"unifiedDataTable": "packages/kbn-unified-data-table"
"unifiedDataTable": "packages/kbn-unified-data-table",
"unsavedChangesBadge": "packages/kbn-unsaved-changes-badge"
},
"translations": []
}
11 changes: 11 additions & 0 deletions examples/discover_customization_examples/public/plugin.tsx
Expand Up @@ -163,6 +163,17 @@ export class DiscoverCustomizationExamplesPlugin implements Plugin {
order: 300,
},
],
getBadges: () => {
return [
{
data: {
badgeText: 'Example badge',
color: 'warning',
},
order: 10,
},
];
},
});

customizations.set({
Expand Down
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -780,6 +780,7 @@
"@kbn/unified-field-list-examples-plugin": "link:examples/unified_field_list_examples",
"@kbn/unified-histogram-plugin": "link:src/plugins/unified_histogram",
"@kbn/unified-search-plugin": "link:src/plugins/unified_search",
"@kbn/unsaved-changes-badge": "link:packages/kbn-unsaved-changes-badge",
"@kbn/upgrade-assistant-plugin": "link:x-pack/plugins/upgrade_assistant",
"@kbn/uptime-plugin": "link:x-pack/plugins/uptime",
"@kbn/url-drilldown-plugin": "link:x-pack/plugins/drilldowns/url_drilldown",
Expand Down
Expand Up @@ -85,6 +85,10 @@ export const useRowHeightsOptions = ({
defaultHeight,
lineHeight: rowLineHeight,
onChange: ({ defaultHeight: newRowHeight }: EuiDataGridRowHeightsOptions) => {
if (newRowHeight === defaultHeight && typeof rowHeightState === 'undefined') {
// ignore, no changes required
return;
}
const newSerializedRowHeight = serializeRowHeight(
// pressing "Reset to default" triggers onChange with the same value
newRowHeight === defaultHeight ? configRowHeight : newRowHeight
Expand Down
8 changes: 8 additions & 0 deletions packages/kbn-unsaved-changes-badge/README.md
@@ -0,0 +1,8 @@
# @kbn/unsaved-changes-badge

A yellow "Unsaved changes" badge which can be found for example on Discover page.
It supports callbacks to save or revert the changes.

To integrate it into TopNav component, consider using `getTopNavUnsavedChangesBadge(...)` util and pass the result to `badges` prop of the top nav.

There is also a standalone component `<UnsavedChangesBadge .../>`.
17 changes: 17 additions & 0 deletions packages/kbn-unsaved-changes-badge/index.ts
@@ -0,0 +1,17 @@
/*
* 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 {
UnsavedChangesBadge,
type UnsavedChangesBadgeProps,
} from './src/components/unsaved_changes_badge';

export {
getTopNavUnsavedChangesBadge,
type TopNavUnsavedChangesBadgeParams,
} from './src/utils/get_top_nav_unsaved_changes_badge';
13 changes: 13 additions & 0 deletions packages/kbn-unsaved-changes-badge/jest.config.js
@@ -0,0 +1,13 @@
/*
* 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.
*/

module.exports = {
preset: '@kbn/test',
rootDir: '../..',
roots: ['<rootDir>/packages/kbn-unsaved-changes-badge'],
};
5 changes: 5 additions & 0 deletions packages/kbn-unsaved-changes-badge/kibana.jsonc
@@ -0,0 +1,5 @@
{
"type": "shared-common",
"id": "@kbn/unsaved-changes-badge",
"owner": "@elastic/kibana-data-discovery"
}
10 changes: 10 additions & 0 deletions packages/kbn-unsaved-changes-badge/package.json
@@ -0,0 +1,10 @@
{
"name": "@kbn/unsaved-changes-badge",
"private": true,
"version": "1.0.0",
"license": "SSPL-1.0 OR Elastic License 2.0",
"sideEffects": [
"*.css",
"*.scss"
]
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

@@ -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 { UnsavedChangesBadge, type UnsavedChangesBadgeProps } from './unsaved_changes_badge';
@@ -0,0 +1,89 @@
/*
* 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 { render, act, screen, waitFor } from '@testing-library/react';
import { UnsavedChangesBadge } from './unsaved_changes_badge';

describe('<UnsavedChangesBadge />', () => {
test('should render correctly', async () => {
const onRevert = jest.fn();
const { getByTestId, queryByTestId } = render(
<UnsavedChangesBadge badgeText="test" onRevert={onRevert} />
);
expect(getByTestId('unsavedChangesBadge')).toBeInTheDocument();

getByTestId('unsavedChangesBadge').click();
await waitFor(() => {
return Boolean(queryByTestId('unsavedChangesBadgeMenuPanel'));
});
expect(queryByTestId('revertUnsavedChangesButton')).toBeInTheDocument();
expect(queryByTestId('saveUnsavedChangesButton')).not.toBeInTheDocument();
expect(queryByTestId('saveUnsavedChangesAsButton')).not.toBeInTheDocument();

expect(onRevert).not.toHaveBeenCalled();

act(() => {
getByTestId('revertUnsavedChangesButton').click();
});
expect(onRevert).toHaveBeenCalled();
});

test('should show all menu items', async () => {
const onRevert = jest.fn().mockResolvedValue(true);
const onSave = jest.fn().mockResolvedValue(true);
const onSaveAs = jest.fn().mockResolvedValue(true);
const { getByTestId, queryByTestId, container } = render(
<UnsavedChangesBadge
badgeText="test"
onRevert={onRevert}
onSave={onSave}
onSaveAs={onSaveAs}
/>
);

expect(container).toMatchSnapshot();

getByTestId('unsavedChangesBadge').click();
await waitFor(() => {
return Boolean(queryByTestId('unsavedChangesBadgeMenuPanel'));
});
expect(queryByTestId('revertUnsavedChangesButton')).toBeInTheDocument();
expect(queryByTestId('saveUnsavedChangesButton')).toBeInTheDocument();
expect(queryByTestId('saveUnsavedChangesAsButton')).toBeInTheDocument();

expect(screen.getByTestId('unsavedChangesBadgeMenuPanel')).toMatchSnapshot();
});

test('should call callbacks', async () => {
const onRevert = jest.fn().mockResolvedValue(true);
const onSave = jest.fn().mockResolvedValue(true);
const onSaveAs = jest.fn().mockResolvedValue(true);
const { getByTestId, queryByTestId } = render(
<UnsavedChangesBadge
badgeText="test"
onRevert={onRevert}
onSave={onSave}
onSaveAs={onSaveAs}
/>
);
act(() => {
getByTestId('unsavedChangesBadge').click();
});
await waitFor(() => {
return Boolean(queryByTestId('unsavedChangesBadgeMenuPanel'));
});

expect(onSave).not.toHaveBeenCalled();

act(() => {
getByTestId('saveUnsavedChangesButton').click();
});
expect(onSave).toHaveBeenCalled();
});
});

0 comments on commit fbfcf39

Please sign in to comment.