From dec3f8890929e5e8deb4d625a8e66c14841162d1 Mon Sep 17 00:00:00 2001 From: "opensearch-trigger-bot[bot]" <98922864+opensearch-trigger-bot[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 11:52:11 -0700 Subject: [PATCH] Page header (#236) (#239) * added release notes 2.16 (#227) * added release notes 2.16 * added release notes for 2.16 --------- * chanels complete * almost done * corrected Chanel list page * view channel * view channel * channel details fixed * fixed many things * bug fixes * fix tests page header changes * code refactored * fix UTs * Compressed UX * resolve merge conflict from main * resolve merge conflict from main * refactored the code * refactored the code * addressed the comment * addressed the comment * addressed the comment * addressed the comment --------- (cherry picked from commit 33d7bc16b405bcbae4032209eeef200a0f9c577e) Signed-off-by: Riya Saxena Signed-off-by: github-actions[bot] Co-authored-by: github-actions[bot] --- .babelrc | 18 - babel.config.js | 21 + models/interfaces.ts | 1 + opensearch_dashboards.json | 3 +- package.json | 5 +- public/components/PageHeader/PageHeader.tsx | 43 ++ public/pages/Channels/Channels.tsx | 129 ++++-- .../__tests__/ChannelDetails.test.tsx | 5 + .../__tests__/ChannelDetailsActions.test.tsx | 5 + .../Channels/__tests__/Channels.test.tsx | 5 + .../ChannelDetails.test.tsx.snap | 92 ++-- .../components/details/ChannelDetails.tsx | 197 ++++++--- .../details/ChannelDetailsActions.tsx | 19 +- public/pages/CreateChannel/CreateChannel.tsx | 20 +- .../__tests__/CreateChannel.test.tsx | 5 + public/pages/Emails/CreateRecipientGroup.tsx | 27 +- public/pages/Emails/CreateSESSender.tsx | 28 +- public/pages/Emails/CreateSender.tsx | 28 +- public/pages/Emails/EmailGroups.tsx | 14 +- public/pages/Emails/EmailSenders.tsx | 21 +- .../__tests__/CreateRecipientGroup.test.tsx | 5 + .../CreateRecipientGroupForm.test.tsx | 5 + .../Emails/__tests__/CreateSESSender.test.tsx | 5 + .../__tests__/CreateSESSenderForm.test.tsx | 5 + .../Emails/__tests__/CreateSender.test.tsx | 5 + .../__tests__/CreateSenderForm.test.tsx | 5 + .../Emails/__tests__/EmailGroups.test.tsx | 7 +- .../Emails/__tests__/EmailSenders.test.tsx | 7 +- .../__tests__/RecipientGroupsTable.test.tsx | 5 + .../Emails/__tests__/SendersTable.test.tsx | 5 + .../__tests__/SendersTableControls.test.tsx | 5 + .../Emails/__tests__/validationHelper.test.ts | 5 + .../tables/RecipientGroupsTable.tsx | 272 ++++++++---- .../components/tables/SESSendersTable.tsx | 270 +++++++---- .../Emails/components/tables/SendersTable.tsx | 261 +++++++---- public/pages/Main/Main.tsx | 418 +++++++++--------- public/pages/Main/__tests__/Main.test.tsx | 6 +- public/plugin.ts | 11 +- public/services/utils/constants.ts | 28 ++ public/utils/constants.ts | 9 +- test/setup.jest.ts | 2 - test/utils/helpers.ts | 21 + yarn.lock | 136 +++--- 43 files changed, 1418 insertions(+), 766 deletions(-) delete mode 100644 .babelrc create mode 100644 babel.config.js create mode 100644 public/components/PageHeader/PageHeader.tsx create mode 100644 public/services/utils/constants.ts create mode 100644 test/utils/helpers.ts diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 4e3f6ccc..00000000 --- a/.babelrc +++ /dev/null @@ -1,18 +0,0 @@ -{ - "presets": [ - [ - "@babel/preset-env", - { - "targets": { "node": "10" } - } - ], - "@babel/preset-react", - "@babel/preset-typescript" - ], - "plugins": [ - "@babel/plugin-transform-modules-commonjs", - ["@babel/plugin-transform-runtime", { "regenerator": true }], - "@babel/plugin-transform-class-properties", - "@babel/plugin-transform-object-rest-spread" - ] -} diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 00000000..6a2a4807 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,21 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +// babelrc doesn't respect NODE_PATH anymore but using require does. +// Alternative to install them locally in node_modules +module.exports = { + "presets": [ + require('@babel/preset-env'), + require('@babel/preset-react'), + require('@babel/preset-typescript'), + ], + "plugins": + [ + [require("@babel/plugin-transform-modules-commonjs"), { "allowTopLevelThis": true }], + [require('@babel/plugin-transform-runtime'), { regenerator: true }], + require('@babel/plugin-transform-class-properties'), + require('@babel/plugin-transform-object-rest-spread') + ] +}; diff --git a/models/interfaces.ts b/models/interfaces.ts index 0860f46e..8404380b 100644 --- a/models/interfaces.ts +++ b/models/interfaces.ts @@ -106,4 +106,5 @@ export interface TableState { selectedItems: T[]; items: T[]; loading: boolean; + isPopoverOpen?: boolean; // Ensure this is included } diff --git a/opensearch_dashboards.json b/opensearch_dashboards.json index 7f81f982..d5fb09ea 100644 --- a/opensearch_dashboards.json +++ b/opensearch_dashboards.json @@ -4,7 +4,8 @@ "opensearchDashboardsVersion": "2.17.0", "requiredPlugins": [ "navigation", - "data" + "data", + "opensearchDashboardsUtils" ], "optionalPlugins": [ "share", diff --git a/package.json b/package.json index 4ea04fb1..e33c8eaf 100644 --- a/package.json +++ b/package.json @@ -20,13 +20,12 @@ "plugin_helpers": "node ../../scripts/plugin_helpers", "postbuild": "echo Renaming build artifact to [$npm_package_config_zip_name-$npm_package_version.zip] && mv build/$npm_package_config_id*.zip build/$npm_package_config_zip_name-$npm_package_version.zip" }, - "dependencies": {}, "devDependencies": { "@types/enzyme-adapter-react-16": "^1.0.6", "@types/showdown": "^1.9.3", + "cypress": "9.5.4", "enzyme-adapter-react-16": "^1.15.5", - "jest-dom": "^4.0.0", - "cypress": "9.5.4" + "jest-dom": "^4.0.0" }, "resolutions": { "async": "^3.2.3", diff --git a/public/components/PageHeader/PageHeader.tsx b/public/components/PageHeader/PageHeader.tsx new file mode 100644 index 00000000..e36c9c68 --- /dev/null +++ b/public/components/PageHeader/PageHeader.tsx @@ -0,0 +1,43 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { + TopNavControlData, + TopNavControlDescriptionData, + TopNavControlLinkData, +} from '../../../../../src/plugins/navigation/public'; +import { getApplication, getNavigationUI, getUseUpdatedUx } from '../../services/utils/constants'; + +export interface PageHeaderProps { + appRightControls?: TopNavControlData[]; + appBadgeControls?: TopNavControlData[]; + appDescriptionControls?: (TopNavControlDescriptionData | TopNavControlLinkData)[]; + appLeftControls?: TopNavControlData[]; +} + +const PageHeader: React.FC = ({ + children, + appBadgeControls, + appRightControls, + appDescriptionControls, + appLeftControls, +}) => { + const { HeaderControl } = getNavigationUI(); + const { setAppBadgeControls, setAppRightControls, setAppDescriptionControls, setAppLeftControls } = getApplication(); + + return getUseUpdatedUx() ? ( + <> + + + + + + ) : ( + <>{children} + ); +}; + +export default PageHeader; \ No newline at end of file diff --git a/public/pages/Channels/Channels.tsx b/public/pages/Channels/Channels.tsx index cea25925..7d3bea3b 100644 --- a/public/pages/Channels/Channels.tsx +++ b/public/pages/Channels/Channels.tsx @@ -12,12 +12,13 @@ import { EuiLink, EuiTableFieldDataColumnType, EuiTableSortingType, + EuiTitle, SortDirection, } from '@elastic/eui'; import { Criteria } from '@elastic/eui/src/components/basic_table/basic_table'; import { Pagination } from '@elastic/eui/src/components/basic_table/pagination_bar'; import _ from 'lodash'; -import React, { Component, useContext } from 'react'; +import React from 'react'; import { RouteComponentProps } from 'react-router-dom'; import { ChannelItemType, TableState } from '../../../models/interfaces'; import { @@ -29,6 +30,7 @@ import { NotificationService } from '../../services'; import { BREADCRUMBS, ROUTES, + setBreadcrumbs, } from '../../utils/constants'; import { CHANNEL_TYPE, @@ -43,6 +45,9 @@ import MDSEnabledComponent, { isDataSourceChanged, isDataSourceError, } from '../../components/MDSEnabledComponent/MDSEnabledComponent'; +import PageHeader from "../../components/PageHeader/PageHeader" +import { getUseUpdatedUx } from '../../services/utils/constants'; +import { TopNavControlButtonData } from 'src/plugins/navigation/public'; interface ChannelsProps extends RouteComponentProps, DataSourceMenuProperties { notificationService: NotificationService; @@ -115,7 +120,7 @@ export class Channels extends MDSEnabledComponent } async componentDidMount() { - this.context.chrome.setBreadcrumbs([ + setBreadcrumbs([ BREADCRUMBS.NOTIFICATIONS, BREADCRUMBS.CHANNELS, ]); @@ -215,26 +220,82 @@ export class Channels extends MDSEnabledComponent onSelectionChange: this.onSelectionChange, }; + const headerControls = [ + { + id: 'Create Channel', + label: 'Create channel', + iconType: 'plus', + fill: true, + href: `#${ROUTES.CREATE_CHANNEL}`, + testId: 'createButton', + controlType: 'button', + } as TopNavControlButtonData, + ]; + + const totalChannels = ( + +

({this.state.total})

+
+ ) + + const channelActionsComponent = this.setState({ selectedItems })} + items={this.state.items} + setItems={(items: ChannelItemType[]) => this.setState({ items })} + refresh={this.refresh} />; + + const channelControlsComponent = ; + + const basicTableComponent = No channels to display} + body="To send or receive notifications, you will need to create a notification channel." + actions={ + Create channel + } />} + onChange={this.onTableChange} + pagination={pagination} + sorting={sorting} + tableLayout="auto" + loading={this.state.loading} />; + return ( <> + {getUseUpdatedUx() ? ( + <> + + +
+
+ {channelControlsComponent} +
+ {channelActionsComponent} +
+
+
+ + {basicTableComponent} +
+ + ) : ( - this.setState({ selectedItems }) - } - items={this.state.items} - setItems={(items: ChannelItemType[]) => - this.setState({ items }) - } - refresh={this.refresh} - /> - ), + component: channelActionsComponent, }, { component: ( @@ -251,38 +312,14 @@ export class Channels extends MDSEnabledComponent titleSize="m" total={this.state.total} > - + {channelControlsComponent} - - No channels to display} - body="To send or receive notifications, you will need to create a notification channel." - actions={ - - Create channel - - } - /> - } - onChange={this.onTableChange} - pagination={pagination} - sorting={sorting} - tableLayout="auto" - loading={this.state.loading} - /> + {basicTableComponent} - + )} + + ); } -} +}; + diff --git a/public/pages/Channels/__tests__/ChannelDetails.test.tsx b/public/pages/Channels/__tests__/ChannelDetails.test.tsx index 5ebcacb1..c4ace40f 100644 --- a/public/pages/Channels/__tests__/ChannelDetails.test.tsx +++ b/public/pages/Channels/__tests__/ChannelDetails.test.tsx @@ -18,6 +18,11 @@ import { import { CoreServicesContext } from '../../../components/coreServices'; import { ServicesContext } from '../../../services'; import { ChannelDetails } from '../components/details/ChannelDetails'; +import { setupCoreStart } from '../../../../test/utils/helpers'; + +beforeAll(() => { + setupCoreStart(); +}); describe(' spec', () => { configure({ adapter: new Adapter() }); diff --git a/public/pages/Channels/__tests__/ChannelDetailsActions.test.tsx b/public/pages/Channels/__tests__/ChannelDetailsActions.test.tsx index f1224188..122b0a8e 100644 --- a/public/pages/Channels/__tests__/ChannelDetailsActions.test.tsx +++ b/public/pages/Channels/__tests__/ChannelDetailsActions.test.tsx @@ -15,6 +15,11 @@ import { import { CoreServicesContext } from '../../../components/coreServices'; import { ServicesContext } from '../../../services'; import { ChannelDetailsActions } from '../components/details/ChannelDetailsActions'; +import { setupCoreStart } from '../../../../test/utils/helpers'; + +beforeAll(() => { + setupCoreStart(); +}); describe(' spec', () => { configure({ adapter: new Adapter() }); diff --git a/public/pages/Channels/__tests__/Channels.test.tsx b/public/pages/Channels/__tests__/Channels.test.tsx index b4ef044e..f730a35e 100644 --- a/public/pages/Channels/__tests__/Channels.test.tsx +++ b/public/pages/Channels/__tests__/Channels.test.tsx @@ -14,6 +14,11 @@ import { import { CoreServicesContext } from '../../../components/coreServices'; import { MainContext } from '../../Main/Main'; import { Channels } from '../Channels'; +import { setupCoreStart } from '../../../../test/utils/helpers'; + +beforeAll(() => { + setupCoreStart(); +}); describe(' spec', () => { it('renders the empty component', () => { diff --git a/public/pages/Channels/__tests__/__snapshots__/ChannelDetails.test.tsx.snap b/public/pages/Channels/__tests__/__snapshots__/ChannelDetails.test.tsx.snap index 2cdaf728..f62687c9 100644 --- a/public/pages/Channels/__tests__/__snapshots__/ChannelDetails.test.tsx.snap +++ b/public/pages/Channels/__tests__/__snapshots__/ChannelDetails.test.tsx.snap @@ -2,11 +2,6 @@ exports[` spec handles a non-existing channel 1`] = `
-
spec handles a non-existing channel 1`] = `
-
-
+ class="euiFlexGroup euiFlexGroup--gutterMedium euiFlexGroup--alignItemsFlexEnd euiFlexGroup--directionRow euiFlexGroup--responsive" + > +
+
+
+
spec handles a non-existing channel 1`] = ` exports[` spec renders a specific channel 1`] = `
-
spec renders a specific channel 1`] = `
-
-
+ class="euiFlexGroup euiFlexGroup--gutterMedium euiFlexGroup--alignItemsFlexEnd euiFlexGroup--directionRow euiFlexGroup--responsive" + > +
+
+
+
spec renders a specific channel 1`] = ` exports[` spec renders the component 1`] = `
+ class="euiFlexGroup euiFlexGroup--gutterMedium euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive" + style="max-width: 1316px;" +> +
+
+
+

+ - +

+
+
+
+
+
+
+
+
+
+
`; diff --git a/public/pages/Channels/components/details/ChannelDetails.tsx b/public/pages/Channels/components/details/ChannelDetails.tsx index 27bab75b..96b71f4f 100644 --- a/public/pages/Channels/components/details/ChannelDetails.tsx +++ b/public/pages/Channels/components/details/ChannelDetails.tsx @@ -5,11 +5,10 @@ import { EuiButton, - EuiSmallButton, EuiFlexGroup, EuiFlexItem, - EuiGlobalToastList, EuiHealth, + EuiSmallButton, EuiSpacer, EuiText, EuiTitle, @@ -25,6 +24,7 @@ import { ServicesContext } from '../../../../services'; import { BREADCRUMBS, ROUTES, + setBreadcrumbs, } from '../../../../utils/constants'; import { renderTime } from '../../../../utils/helpers'; import { ListItemType } from '../../types'; @@ -32,8 +32,11 @@ import { MuteChannelModal } from '../modals/MuteChannelModal'; import { ChannelDetailItems } from './ChannelDetailItems'; import { ChannelDetailsActions } from './ChannelDetailsActions'; import { ChannelSettingsDetails } from './ChannelSettingsDetails'; +import PageHeader from "../../../../components/PageHeader/PageHeader"; -interface ChannelDetailsProps extends RouteComponentProps<{ id: string }> {} +interface ChannelDetailsProps extends RouteComponentProps<{ + id: string +}> { } export function ChannelDetails(props: ChannelDetailsProps) { const coreContext = useContext(CoreServicesContext)!; @@ -42,8 +45,22 @@ export function ChannelDetails(props: ChannelDetailsProps) { const [channel, setChannel] = useState(); const [toasts, setToasts] = useState([]); + const sendTestMessage = async () => { + try { + await servicesContext.notificationService.sendTestMessage(id); + coreContext.notifications.toasts.addSuccess( + 'Successfully sent a test message.' + ); + } catch (error) { + coreContext.notifications.toasts.addError(error?.body || error, { + title: 'Failed to send the test message.', + toastMessage: 'View error details and adjust the channel settings.', + }); + } + }; + useEffect(() => { - coreContext.chrome.setBreadcrumbs([ + setBreadcrumbs([ BREADCRUMBS.NOTIFICATIONS, BREADCRUMBS.CHANNELS, ]); @@ -69,7 +86,7 @@ export function ChannelDetails(props: ChannelDetailsProps) { }) .then((response) => { setChannel(response); - coreContext.chrome.setBreadcrumbs([ + setBreadcrumbs([ BREADCRUMBS.NOTIFICATIONS, BREADCRUMBS.CHANNELS, { @@ -126,76 +143,111 @@ export function ChannelDetails(props: ChannelDetailsProps) { }, ]; + const actionsAndMuteComponent = + + + {channel && ( + + )} + + + {channel && ( + + {({ onShow }) => ( + { + if (channel.is_enabled) { + onShow(MuteChannelModal, { + selected: [channel], + setSelected: (selected: any[]) => setChannel(selected[0]), + }); + } else { + const newChannel = { ...channel, is_enabled: true }; + servicesContext.notificationService + .updateConfig(channel.config_id, newChannel) + .then(() => { + coreContext.notifications.toasts.addSuccess( + `Channel ${channel.name} successfully unmuted.` + ); + setChannel(newChannel); + }); + } + }} + > + {channel.is_enabled ? 'Mute channel' : 'Unmute channel'} + + )} + + )} + + ; + const rightControls = [ + { + renderComponent: ( + actionsAndMuteComponent + ), + }, + { + renderComponent: ( +
+ + Send test message + +
+ ), + }, + ]; + + const badgeComponent = + {channel?.is_enabled === undefined ? null : channel.is_enabled ? ( + Active + ) : ( + Muted + )} + ; + + const badgeControls = [ + { + renderComponent: ( + badgeComponent + ), + }, + ]; + return ( <> - { - setToasts(toasts.filter((toast) => toast.id !== removedToast.id)); - }} - toastLifeTimeMs={60000} - /> - - - - - - -

{channel?.name || '-'}

-
-
- - {channel?.is_enabled === undefined ? null : channel.is_enabled ? ( - Active - ) : ( - Muted - )} - + {( + + + + + +

{channel?.name ?? '-'}

+
+
+ {badgeComponent} +
+
+ {actionsAndMuteComponent}
-
- - - {channel && } - - - {channel && ( - - {({ onShow }) => ( - { - if (!channel) return; - if (channel.is_enabled) { - onShow(MuteChannelModal, { - selected: [channel], - setSelected: (selected: ChannelItemType[]) => - setChannel(selected[0]), - }); - } else { - const newChannel = { ...channel, is_enabled: true }; - servicesContext.notificationService - .updateConfig(channel.config_id, newChannel) - .then((resp) => { - coreContext.notifications.toasts.addSuccess( - `Channel ${channel.name} successfully unmuted.` - ); - setChannel(newChannel); - }); - } - }} - > - {channel?.is_enabled ? 'Mute channel' : 'Unmute channel'} - - )} - - )} - -
+ )} + ); -} +}; + diff --git a/public/pages/Channels/components/details/ChannelDetailsActions.tsx b/public/pages/Channels/components/details/ChannelDetailsActions.tsx index dcbf94e2..7d01d3df 100644 --- a/public/pages/Channels/components/details/ChannelDetailsActions.tsx +++ b/public/pages/Channels/components/details/ChannelDetailsActions.tsx @@ -17,6 +17,7 @@ import { ModalConsumer } from '../../../../components/Modal'; import { ServicesContext } from '../../../../services'; import { ROUTES } from '../../../../utils/constants'; import { DeleteChannelModal } from '../modals/DeleteChannelModal'; +import { getUseUpdatedUx } from '../../../../services/utils/constants'; interface ChannelDetailsActionsParams { label: string; @@ -58,13 +59,6 @@ export function ChannelDetailsActions(props: ChannelDetailsActionsProps) { label: 'Edit', href: `#${ROUTES.EDIT_CHANNEL}/${props.channel.config_id}?from=details`, }, - { - label: 'Send test message', - disabled: - !props.channel.config_id || - !props.channel.is_enabled, - action: sendTestMessage, - }, { label: 'Delete', color: 'danger', @@ -75,6 +69,17 @@ export function ChannelDetailsActions(props: ChannelDetailsActionsProps) { }, ]; + // Add Send Test Message action only if getUseUpdatedUx is false + if (!getUseUpdatedUx()) { + actions.splice(1, 0, { + label: 'Send test message', + disabled: + !props.channel.config_id || + !props.channel.is_enabled, + action: sendTestMessage, + }); + } + return ( {({ onShow }) => ( diff --git a/public/pages/CreateChannel/CreateChannel.tsx b/public/pages/CreateChannel/CreateChannel.tsx index c6c30e52..2256cb59 100644 --- a/public/pages/CreateChannel/CreateChannel.tsx +++ b/public/pages/CreateChannel/CreateChannel.tsx @@ -28,8 +28,9 @@ import { BREADCRUMBS, CUSTOM_WEBHOOK_ENDPOINT_TYPE, ROUTES, + setBreadcrumbs, } from '../../utils/constants'; -import {BACKEND_CHANNEL_TYPE,CHANNEL_TYPE } from '../../../common/constants' +import { BACKEND_CHANNEL_TYPE, CHANNEL_TYPE } from '../../../common/constants' import { getErrorMessage } from '../../utils/helpers'; import { HeaderItemType, WebhookHttpType, WebhookMethodType } from '../Channels/types'; import { MainContext } from '../Main/Main'; @@ -55,7 +56,7 @@ import { validateRecipients, validateWebhookURL, } from './utils/validationHelper'; - +import { getUseUpdatedUx } from '../../services/utils/constants'; interface CreateChannelsProps extends RouteComponentProps<{ id?: string }> { edit?: boolean; } @@ -143,11 +144,9 @@ export function CreateChannel(props: CreateChannelsProps) { }); useEffect(() => { - coreContext.chrome.setBreadcrumbs([ - BREADCRUMBS.NOTIFICATIONS, + setBreadcrumbs([ BREADCRUMBS.NOTIFICATIONS, BREADCRUMBS.CHANNELS, - props.edit ? BREADCRUMBS.EDIT_CHANNEL : BREADCRUMBS.CREATE_CHANNEL, - ]); + props.edit ? BREADCRUMBS.EDIT_CHANNEL : BREADCRUMBS.CREATE_CHANNEL]); window.scrollTo(0, 0); if (props.edit) { @@ -365,9 +364,11 @@ export function CreateChannel(props: CreateChannelsProps) { - -

{`${props.edit ? 'Edit' : 'Create'} channel`}

-
+ {!getUseUpdatedUx() && ( + +

{`${props.edit ? 'Edit' : 'Create'} channel`}

+
+ )} ); } - diff --git a/public/pages/CreateChannel/__tests__/CreateChannel.test.tsx b/public/pages/CreateChannel/__tests__/CreateChannel.test.tsx index 01f03719..c237e88c 100644 --- a/public/pages/CreateChannel/__tests__/CreateChannel.test.tsx +++ b/public/pages/CreateChannel/__tests__/CreateChannel.test.tsx @@ -16,6 +16,11 @@ import { CoreServicesContext } from '../../../components/coreServices'; import { ServicesContext } from '../../../services'; import { MainContext } from '../../Main/Main'; import { CreateChannel } from '../CreateChannel'; +import { setupCoreStart } from '../../../../test/utils/helpers'; + +beforeAll(() => { + setupCoreStart(); +}); describe(' spec', () => { const updateConfigSuccess = jest.fn(async (configId: string, config: any) => diff --git a/public/pages/Emails/CreateRecipientGroup.tsx b/public/pages/Emails/CreateRecipientGroup.tsx index d8c49302..9ac9c534 100644 --- a/public/pages/Emails/CreateRecipientGroup.tsx +++ b/public/pages/Emails/CreateRecipientGroup.tsx @@ -18,7 +18,7 @@ import { SERVER_DELAY } from '../../../common'; import { ContentPanel } from '../../components/ContentPanel'; import { CoreServicesContext } from '../../components/coreServices'; import { ServicesContext } from '../../services'; -import { BREADCRUMBS, ROUTES } from '../../utils/constants'; +import { BREADCRUMBS, ROUTES, setBreadcrumbs } from '../../utils/constants'; import { getErrorMessage } from '../../utils/helpers'; import { CreateRecipientGroupForm } from './components/forms/CreateRecipientGroupForm'; import { createRecipientGroupConfigObject } from './utils/helper'; @@ -26,6 +26,7 @@ import { validateRecipientGroupEmails, validateRecipientGroupName, } from './utils/validationHelper'; +import { getUseUpdatedUx } from '../../services/utils/constants'; interface CreateRecipientGroupProps extends RouteComponentProps<{ id?: string }> { @@ -65,7 +66,7 @@ export function CreateRecipientGroup(props: CreateRecipientGroupProps) { }; useEffect(() => { - coreContext.chrome.setBreadcrumbs([ + setBreadcrumbs([ BREADCRUMBS.NOTIFICATIONS, BREADCRUMBS.EMAIL_GROUPS, props.edit @@ -103,9 +104,11 @@ export function CreateRecipientGroup(props: CreateRecipientGroupProps) { return ( <> - -

{`${props.edit ? 'Edit' : 'Create'} recipient group`}

-
+ {!getUseUpdatedUx() && ( + +

{`${props.edit ? 'Edit' : 'Create'} recipient group`}

+
+ )} { coreContext.notifications.toasts.addSuccess( - `Recipient group ${name} successfully ${ - props.edit ? 'updated' : 'created' + `Recipient group ${name} successfully ${props.edit ? 'updated' : 'created' }.` ); setTimeout( @@ -174,9 +176,8 @@ export function CreateRecipientGroup(props: CreateRecipientGroupProps) { coreContext.notifications.toasts.addError( error?.body || error, { - title: `Failed to ${ - props.edit ? 'update' : 'create' - } recipient group.`, + title: `Failed to ${props.edit ? 'update' : 'create' + } recipient group.`, } ); }); diff --git a/public/pages/Emails/CreateSESSender.tsx b/public/pages/Emails/CreateSESSender.tsx index b64eafde..63a6c960 100644 --- a/public/pages/Emails/CreateSESSender.tsx +++ b/public/pages/Emails/CreateSESSender.tsx @@ -17,7 +17,7 @@ import { SERVER_DELAY } from '../../../common'; import { ContentPanel } from '../../components/ContentPanel'; import { CoreServicesContext } from '../../components/coreServices'; import { ServicesContext } from '../../services'; -import { BREADCRUMBS, ROUTES } from '../../utils/constants'; +import { BREADCRUMBS, ROUTES, setBreadcrumbs } from '../../utils/constants'; import { getErrorMessage } from '../../utils/helpers'; import { MainContext } from '../Main/Main'; import { CreateSESSenderForm } from './components/forms/CreateSESSenderForm'; @@ -28,7 +28,7 @@ import { validateRoleArn, validateSenderName, } from './utils/validationHelper'; - +import { getUseUpdatedUx } from '../../services/utils/constants'; interface CreateSESSenderProps extends RouteComponentProps<{ id?: string }> { edit?: boolean; } @@ -51,7 +51,7 @@ export function CreateSESSender(props: CreateSESSenderProps) { }); useEffect(() => { - coreContext.chrome.setBreadcrumbs([ + setBreadcrumbs([ BREADCRUMBS.NOTIFICATIONS, BREADCRUMBS.EMAIL_SENDERS, props.edit ? BREADCRUMBS.EDIT_SES_SENDER : BREADCRUMBS.CREATE_SES_SENDER, @@ -101,9 +101,11 @@ export function CreateSESSender(props: CreateSESSenderProps) { return ( <> - -

{`${props.edit ? 'Edit' : 'Create'} SES sender`}

-
+ {!getUseUpdatedUx() && ( + +

{`${props.edit ? 'Edit' : 'Create'} SES sender`}

+
+ )} { coreContext.notifications.toasts.addSuccess( - `Sender ${senderName} successfully ${ - props.edit ? 'updated' : 'created' + `Sender ${senderName} successfully ${props.edit ? 'updated' : 'created' }.` ); setTimeout( @@ -172,9 +173,8 @@ export function CreateSESSender(props: CreateSESSenderProps) { .catch((error) => { setLoading(false); coreContext.notifications.toasts.addError(error?.body || error, { - title: `Failed to ${ - props.edit ? 'update' : 'create' - } sender.`, + title: `Failed to ${props.edit ? 'update' : 'create' + } sender.`, }); }); }} diff --git a/public/pages/Emails/CreateSender.tsx b/public/pages/Emails/CreateSender.tsx index 7f8b96f2..15492bbd 100644 --- a/public/pages/Emails/CreateSender.tsx +++ b/public/pages/Emails/CreateSender.tsx @@ -17,7 +17,7 @@ import { SERVER_DELAY } from '../../../common'; import { ContentPanel } from '../../components/ContentPanel'; import { CoreServicesContext } from '../../components/coreServices'; import { ServicesContext } from '../../services'; -import { BREADCRUMBS, ENCRYPTION_TYPE, ROUTES } from '../../utils/constants'; +import { BREADCRUMBS, ENCRYPTION_TYPE, ROUTES, setBreadcrumbs } from '../../utils/constants'; import { getErrorMessage } from '../../utils/helpers'; import { CreateSenderForm } from './components/forms/CreateSenderForm'; import { createSenderConfigObject } from './utils/helper'; @@ -27,7 +27,7 @@ import { validatePort, validateSenderName, } from './utils/validationHelper'; - +import { getUseUpdatedUx } from '../../services/utils/constants'; interface CreateSenderProps extends RouteComponentProps<{ id?: string }> { edit?: boolean; } @@ -52,7 +52,7 @@ export function CreateSender(props: CreateSenderProps) { }); useEffect(() => { - coreContext.chrome.setBreadcrumbs([ + setBreadcrumbs([ BREADCRUMBS.NOTIFICATIONS, BREADCRUMBS.EMAIL_SENDERS, props.edit ? BREADCRUMBS.EDIT_SENDER : BREADCRUMBS.CREATE_SENDER, @@ -98,9 +98,11 @@ export function CreateSender(props: CreateSenderProps) { return ( <> - -

{`${props.edit ? 'Edit' : 'Create'} SMTP sender`}

-
+ {!getUseUpdatedUx() && ( + +

{`${props.edit ? 'Edit' : 'Create'} SMTP sender`}

+
+ )} { coreContext.notifications.toasts.addSuccess( - `Sender ${senderName} successfully ${ - props.edit ? 'updated' : 'created' + `Sender ${senderName} successfully ${props.edit ? 'updated' : 'created' }.` ); setTimeout( @@ -172,9 +173,8 @@ export function CreateSender(props: CreateSenderProps) { .catch((error) => { setLoading(false); coreContext.notifications.toasts.addError(error?.body || error, { - title: `Failed to ${ - props.edit ? 'update' : 'create' - } sender.`, + title: `Failed to ${props.edit ? 'update' : 'create' + } sender.`, }); }); }} diff --git a/public/pages/Emails/EmailGroups.tsx b/public/pages/Emails/EmailGroups.tsx index 8664e797..90a5cd2c 100644 --- a/public/pages/Emails/EmailGroups.tsx +++ b/public/pages/Emails/EmailGroups.tsx @@ -9,8 +9,8 @@ import { RouteComponentProps } from 'react-router-dom'; import { CoreServicesContext } from '../../components/coreServices'; import { BREADCRUMBS } from '../../utils/constants'; import { RecipientGroupsTable } from './components/tables/RecipientGroupsTable'; -import { MainContext } from '../Main/Main'; import { NotificationService } from '../../services'; +import { getUseUpdatedUx } from '../../services/utils/constants'; interface EmailGroupsProps extends RouteComponentProps { notificationService: NotificationService; @@ -28,12 +28,16 @@ export function EmailGroups(props: EmailGroupsProps) { return ( <> - -

Email recipient groups

-
+ {!getUseUpdatedUx() && ( + +

Email recipient groups

+
+ )} - + ); } diff --git a/public/pages/Emails/EmailSenders.tsx b/public/pages/Emails/EmailSenders.tsx index a93803b8..b7da4ddd 100644 --- a/public/pages/Emails/EmailSenders.tsx +++ b/public/pages/Emails/EmailSenders.tsx @@ -7,11 +7,12 @@ import { EuiSpacer, EuiTitle } from '@elastic/eui'; import React, { useContext, useEffect } from 'react'; import { RouteComponentProps } from 'react-router-dom'; import { CoreServicesContext } from '../../components/coreServices'; -import { BREADCRUMBS } from '../../utils/constants'; +import { BREADCRUMBS, setBreadcrumbs } from '../../utils/constants'; import { MainContext } from '../Main/Main'; import { SendersTable } from './components/tables/SendersTable'; import { SESSendersTable } from './components/tables/SESSendersTable'; import { NotificationService } from '../../services'; +import { getUseUpdatedUx } from '../../services/utils/constants'; interface EmailSendersProps extends RouteComponentProps { notificationService: NotificationService; @@ -23,7 +24,7 @@ export function EmailSenders(props: EmailSendersProps) { useEffect(() => { - coreContext.chrome.setBreadcrumbs([ + setBreadcrumbs([ BREADCRUMBS.NOTIFICATIONS, BREADCRUMBS.EMAIL_SENDERS, ]); @@ -32,13 +33,17 @@ export function EmailSenders(props: EmailSendersProps) { return ( <> - -

Email senders

-
+ {!getUseUpdatedUx() && ( + +

Email senders

+
+ )} {mainStateContext.availableConfigTypes.includes('smtp_account') && ( <> - + )} @@ -46,7 +51,9 @@ export function EmailSenders(props: EmailSendersProps) { {mainStateContext.availableConfigTypes.includes('ses_account') && ( <> - + )} diff --git a/public/pages/Emails/__tests__/CreateRecipientGroup.test.tsx b/public/pages/Emails/__tests__/CreateRecipientGroup.test.tsx index 8b24b008..5e2ee3bd 100644 --- a/public/pages/Emails/__tests__/CreateRecipientGroup.test.tsx +++ b/public/pages/Emails/__tests__/CreateRecipientGroup.test.tsx @@ -15,6 +15,11 @@ import { import { CoreServicesContext } from '../../../components/coreServices'; import { ServicesContext } from '../../../services'; import { CreateRecipientGroup } from '../CreateRecipientGroup'; +import { setupCoreStart } from '../../../../test/utils/helpers'; + +beforeAll(() => { + setupCoreStart(); +}); describe(' spec', () => { it('renders the component', () => { diff --git a/public/pages/Emails/__tests__/CreateRecipientGroupForm.test.tsx b/public/pages/Emails/__tests__/CreateRecipientGroupForm.test.tsx index 8f5d1a96..cf68c8a8 100644 --- a/public/pages/Emails/__tests__/CreateRecipientGroupForm.test.tsx +++ b/public/pages/Emails/__tests__/CreateRecipientGroupForm.test.tsx @@ -8,6 +8,11 @@ import { configure, shallow } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; import React from 'react'; import { CreateRecipientGroupForm } from '../components/forms/CreateRecipientGroupForm'; +import { setupCoreStart } from '../../../../test/utils/helpers'; + +beforeAll(() => { + setupCoreStart(); +}); describe(' spec', () => { configure({ adapter: new Adapter() }); diff --git a/public/pages/Emails/__tests__/CreateSESSender.test.tsx b/public/pages/Emails/__tests__/CreateSESSender.test.tsx index a23ac5a2..2a654d81 100644 --- a/public/pages/Emails/__tests__/CreateSESSender.test.tsx +++ b/public/pages/Emails/__tests__/CreateSESSender.test.tsx @@ -16,6 +16,11 @@ import { CoreServicesContext } from '../../../components/coreServices'; import { ServicesContext } from '../../../services'; import { MainContext } from '../../Main/Main'; import { CreateSESSender } from '../CreateSESSender'; +import { setupCoreStart } from '../../../../test/utils/helpers'; + +beforeAll(() => { + setupCoreStart(); +}); describe(' spec', () => { it('renders the component', () => { diff --git a/public/pages/Emails/__tests__/CreateSESSenderForm.test.tsx b/public/pages/Emails/__tests__/CreateSESSenderForm.test.tsx index 8cf8a11d..9e2e1fa6 100644 --- a/public/pages/Emails/__tests__/CreateSESSenderForm.test.tsx +++ b/public/pages/Emails/__tests__/CreateSESSenderForm.test.tsx @@ -8,6 +8,11 @@ import React from 'react'; import { mainStateMock } from '../../../../test/mocks/serviceMock'; import { MainContext } from '../../Main/Main'; import { CreateSESSenderForm } from '../components/forms/CreateSESSenderForm'; +import { setupCoreStart } from '../../../../test/utils/helpers'; + +beforeAll(() => { + setupCoreStart(); +}); describe(' spec', () => { it('renders the component', () => { diff --git a/public/pages/Emails/__tests__/CreateSender.test.tsx b/public/pages/Emails/__tests__/CreateSender.test.tsx index 99707ebf..b5a21985 100644 --- a/public/pages/Emails/__tests__/CreateSender.test.tsx +++ b/public/pages/Emails/__tests__/CreateSender.test.tsx @@ -12,6 +12,11 @@ import { coreServicesMock } from '../../../../test/mocks/serviceMock'; import { CoreServicesContext } from '../../../components/coreServices'; import { ServicesContext } from '../../../services'; import { CreateSender } from '../CreateSender'; +import { setupCoreStart } from '../../../../test/utils/helpers'; + +beforeAll(() => { + setupCoreStart(); +}); describe(' spec', () => { it('renders the component', () => { diff --git a/public/pages/Emails/__tests__/CreateSenderForm.test.tsx b/public/pages/Emails/__tests__/CreateSenderForm.test.tsx index a226eac1..27252279 100644 --- a/public/pages/Emails/__tests__/CreateSenderForm.test.tsx +++ b/public/pages/Emails/__tests__/CreateSenderForm.test.tsx @@ -6,6 +6,11 @@ import { fireEvent, render } from '@testing-library/react'; import React from 'react'; import { CreateSenderForm } from '../components/forms/CreateSenderForm'; +import { setupCoreStart } from '../../../../test/utils/helpers'; + +beforeAll(() => { + setupCoreStart(); +}); describe(' spec', () => { it('renders the component', () => { diff --git a/public/pages/Emails/__tests__/EmailGroups.test.tsx b/public/pages/Emails/__tests__/EmailGroups.test.tsx index 25437456..07c37688 100644 --- a/public/pages/Emails/__tests__/EmailGroups.test.tsx +++ b/public/pages/Emails/__tests__/EmailGroups.test.tsx @@ -5,14 +5,17 @@ import { render } from '@testing-library/react'; import React from 'react'; -import { routerComponentPropsMock } from '../../../../test/mocks/routerPropsMock'; import { coreServicesMock, notificationServiceMock, } from '../../../../test/mocks/serviceMock'; import { CoreServicesContext } from '../../../components/coreServices'; -import { ServicesContext } from '../../../services'; import { EmailGroups } from '../EmailGroups'; +import { setupCoreStart } from '../../../../test/utils/helpers'; + +beforeAll(() => { + setupCoreStart(); +}); describe(' spec', () => { it('renders the component', () => { diff --git a/public/pages/Emails/__tests__/EmailSenders.test.tsx b/public/pages/Emails/__tests__/EmailSenders.test.tsx index 646f155d..74dd1d54 100644 --- a/public/pages/Emails/__tests__/EmailSenders.test.tsx +++ b/public/pages/Emails/__tests__/EmailSenders.test.tsx @@ -5,16 +5,19 @@ import { render, screen } from '@testing-library/react'; import React from 'react'; -import { routerComponentPropsMock } from '../../../../test/mocks/routerPropsMock'; import { coreServicesMock, mainStateMock, notificationServiceMock, } from '../../../../test/mocks/serviceMock'; import { CoreServicesContext } from '../../../components/coreServices'; -import { ServicesContext } from '../../../services'; import { MainContext } from '../../Main/Main'; import { EmailSenders } from '../EmailSenders'; +import { setupCoreStart } from '../../../../test/utils/helpers'; + +beforeAll(() => { + setupCoreStart(); +}); describe(' spec', () => { it('renders the component with SMTP config type', () => { diff --git a/public/pages/Emails/__tests__/RecipientGroupsTable.test.tsx b/public/pages/Emails/__tests__/RecipientGroupsTable.test.tsx index a63f6d70..cdd27786 100644 --- a/public/pages/Emails/__tests__/RecipientGroupsTable.test.tsx +++ b/public/pages/Emails/__tests__/RecipientGroupsTable.test.tsx @@ -15,6 +15,11 @@ import { import { CoreServicesContext } from '../../../components/coreServices'; import { ServicesContext } from '../../../services'; import { RecipientGroupsTable } from '../components/tables/RecipientGroupsTable'; +import { setupCoreStart } from '../../../../test/utils/helpers'; + +beforeAll(() => { + setupCoreStart(); +}); describe(' spec', () => { configure({ adapter: new Adapter() }); diff --git a/public/pages/Emails/__tests__/SendersTable.test.tsx b/public/pages/Emails/__tests__/SendersTable.test.tsx index c706308f..b3e163a0 100644 --- a/public/pages/Emails/__tests__/SendersTable.test.tsx +++ b/public/pages/Emails/__tests__/SendersTable.test.tsx @@ -15,6 +15,11 @@ import { import { CoreServicesContext } from '../../../components/coreServices'; import { ServicesContext } from '../../../services'; import { SendersTable } from '../components/tables/SendersTable'; +import { setupCoreStart } from '../../../../test/utils/helpers'; + +beforeAll(() => { + setupCoreStart(); +}); describe(' spec', () => { configure({ adapter: new Adapter() }); diff --git a/public/pages/Emails/__tests__/SendersTableControls.test.tsx b/public/pages/Emails/__tests__/SendersTableControls.test.tsx index 732af29e..360264c2 100644 --- a/public/pages/Emails/__tests__/SendersTableControls.test.tsx +++ b/public/pages/Emails/__tests__/SendersTableControls.test.tsx @@ -8,6 +8,11 @@ import React from 'react'; import { mainStateMock } from '../../../../test/mocks/serviceMock'; import { MainContext } from '../../Main/Main'; import { SendersTableControls } from '../components/tables/SendersTableControls'; +import { setupCoreStart } from '../../../../test/utils/helpers'; + +beforeAll(() => { + setupCoreStart(); +}); describe(' spec', () => { it('renders the component', () => { diff --git a/public/pages/Emails/__tests__/validationHelper.test.ts b/public/pages/Emails/__tests__/validationHelper.test.ts index d528897c..7ac29947 100644 --- a/public/pages/Emails/__tests__/validationHelper.test.ts +++ b/public/pages/Emails/__tests__/validationHelper.test.ts @@ -11,6 +11,11 @@ import { validateRecipientGroupName, validateSenderName, } from '../utils/validationHelper'; +import { setupCoreStart } from '../../../../test/utils/helpers'; + +beforeAll(() => { + setupCoreStart(); +}); describe('test sender and recipient group input validations', () => { it('validates sender name', () => { diff --git a/public/pages/Emails/components/tables/RecipientGroupsTable.tsx b/public/pages/Emails/components/tables/RecipientGroupsTable.tsx index fd145686..9d2cb2b6 100644 --- a/public/pages/Emails/components/tables/RecipientGroupsTable.tsx +++ b/public/pages/Emails/components/tables/RecipientGroupsTable.tsx @@ -6,12 +6,17 @@ import { EuiBasicTable, EuiSmallButton, + EuiContextMenuItem, EuiEmptyPrompt, EuiCompressedFieldSearch, + EuiFlexGroup, + EuiFlexItem, EuiHorizontalRule, EuiLink, + EuiPopover, EuiTableFieldDataColumnType, EuiTableSortingType, + EuiTitle, SortDirection, } from '@elastic/eui'; import { Criteria } from '@elastic/eui/src/components/basic_table/basic_table'; @@ -38,6 +43,9 @@ import { isDataSourceError, isDataSourceChanged, } from '../../../../components/MDSEnabledComponent/MDSEnabledComponent'; +import { TopNavControlButtonData } from 'src/plugins/navigation/public'; +import { getUseUpdatedUx } from '../../../../../public/services/utils/constants'; +import PageHeader from '../../../../../public/components/PageHeader/PageHeader'; interface RecipientGroupsTableProps { coreContext: CoreStart; @@ -45,7 +53,7 @@ interface RecipientGroupsTableProps { } interface RecipientGroupsTableState - extends TableState {} + extends TableState { } export class RecipientGroupsTable extends Component< RecipientGroupsTableProps, @@ -196,6 +204,12 @@ export class RecipientGroupsTable extends Component< this.setState({ from: 0, search }); }; + togglePopover = () => { + this.setState((prevState) => ({ + isPopoverOpen: !prevState.isPopoverOpen, + })); + }; + render() { const page = Math.floor(this.state.from / this.state.size); @@ -218,93 +232,185 @@ export class RecipientGroupsTable extends Component< onSelectionChange: this.onSelectionChange, }; + const actions = [ + { + label: 'Edit', + disabled: this.state.selectedItems.length !== 1, + action: () => { + location.assign(`#${ROUTES.EDIT_RECIPIENT_GROUP}/${this.state.selectedItems[0]?.config_id}`); + }, + }, + { + label: 'Delete', + disabled: this.state.selectedItems.length === 0, + modal: DeleteRecipientGroupModal, + modalParams: { + recipientGroups: this.state.selectedItems, + refresh: this.refresh, + }, + }, + ]; + + const headerControls = [ + { + id: 'Create recipient group', + label: 'Create recipient group', + iconType: 'plus', + fill: true, + href: `#${ROUTES.CREATE_RECIPIENT_GROUP}`, + testId: 'createButton', + controlType: 'button', + } as TopNavControlButtonData, + ]; + + const totalEmailGroups = ( + +

({this.state.total})

+
+ ) + + const searchComponent = ; + + const createRecepientButton = + Create recipient group + ; + + const tableComponent = No recipient groups to display} + body="Use an email group to manage a list of email addresses you frequently send at a time. You can select recipient groups when configuring email channels." + actions={ + Create recipient group + } />} + onChange={this.onTableChange} + pagination={pagination} + sorting={sorting} />; + return ( <> - - {({ onShow }) => ( - - onShow(DeleteRecipientGroupModal, { - recipientGroups: this.state.selectedItems, - refresh: this.refresh, - }) - } - > - Delete - - )} -
- ), - }, - { - component: ( - - location.assign( - `#${ROUTES.EDIT_RECIPIENT_GROUP}/${this.state.selectedItems[0]?.config_id}` - ) - } - > - Edit - - ), - }, - { - component: ( - - Create recipient group - - ), - }, - ]} + {getUseUpdatedUx() ? ( + <> + - } - bodyStyles={{ padding: 'initial' }} - title="Recipient groups" - titleSize="m" - total={this.state.total} - > - - - - No recipient groups to display} - body="Use an email group to manage a list of email addresses you frequently send at a time. You can select recipient groups when configuring email channels." - actions={ - - Create recipient group - - } + + + + {searchComponent} + + + + Actions + + } + isOpen={this.state.isPopoverOpen} + closePopover={() => this.setState({ isPopoverOpen: false })} + > + {actions.map((action) => ( + + {({ onShow }) => ( + { + this.setState({ isPopoverOpen: false }); + if (action.modal) { + onShow(action.modal, { + ...(action.modalParams || {}), + }); + } else if (action.action) { + action.action(); + } + }} + > + {action.label} + + )} + + ))} + + + + + {tableComponent} + + + ) : ( + + {({ onShow }) => ( + + onShow(DeleteRecipientGroupModal, { + recipientGroups: this.state.selectedItems, + refresh: this.refresh, + }) + } + > + Delete + + )} + + ), + }, + { + component: ( + + location.assign( + `#${ROUTES.EDIT_RECIPIENT_GROUP}/${this.state.selectedItems[0]?.config_id}` + ) + } + > + Edit + + ), + }, + { + component: createRecepientButton, + }, + ]} /> } - onChange={this.onTableChange} - pagination={pagination} - sorting={sorting} - /> - + bodyStyles={{ padding: 'initial' }} + title="Recipient groups" + titleSize="m" + total={this.state.total} + > + {searchComponent} + + {tableComponent} +
+ )} + ); } -} +}; diff --git a/public/pages/Emails/components/tables/SESSendersTable.tsx b/public/pages/Emails/components/tables/SESSendersTable.tsx index 6ea48987..aed3287b 100644 --- a/public/pages/Emails/components/tables/SESSendersTable.tsx +++ b/public/pages/Emails/components/tables/SESSendersTable.tsx @@ -6,9 +6,13 @@ import { EuiBasicTable, EuiSmallButton, + EuiContextMenuItem, EuiEmptyPrompt, EuiCompressedFieldSearch, + EuiFlexGroup, + EuiFlexItem, EuiHorizontalRule, + EuiPopover, EuiTableFieldDataColumnType, EuiTableSortingType, SortDirection, @@ -23,8 +27,7 @@ import { TableState, } from '../../../../../models/interfaces'; import { - ContentPanel, - ContentPanelActions, + ContentPanel, ContentPanelActions, } from '../../../../components/ContentPanel'; import { ModalConsumer } from '../../../../components/Modal'; import { NotificationService, ServicesContext } from '../../../../services'; @@ -36,13 +39,14 @@ import { isDataSourceError, isDataSourceChanged, } from '../../../../components/MDSEnabledComponent/MDSEnabledComponent'; +import { getUseUpdatedUx } from '../../../../../public/services/utils/constants'; interface SESSendersTableProps { coreContext: CoreStart; notificationService: NotificationService; } -interface SESSendersTableState extends TableState {} +interface SESSendersTableState extends TableState { } export class SESSendersTable extends Component< SESSendersTableProps, @@ -63,6 +67,7 @@ export class SESSendersTable extends Component< items: [], selectedItems: [], loading: true, + isPopoverOpen: false, // Initialize popover state }; this.columns = [ @@ -160,6 +165,12 @@ export class SESSendersTable extends Component< this.setState({ from: 0, search }); }; + togglePopover = () => { + this.setState((prevState) => ({ + isPopoverOpen: !prevState.isPopoverOpen, + })); + }; + render() { const page = Math.floor(this.state.from / this.state.size); @@ -182,93 +193,184 @@ export class SESSendersTable extends Component< onSelectionChange: this.onSelectionChange, }; + const actions = [ + { + label: 'Edit', + disabled: this.state.selectedItems.length !== 1, + action: () => { + location.assign(`#${ROUTES.EDIT_SES_SENDER}/${this.state.selectedItems[0]?.config_id}`); + }, + }, + { + label: 'Delete', + disabled: this.state.selectedItems.length === 0, + modal: DeleteSenderModal, + modalParams: { + senders: this.state.selectedItems, + refresh: this.refresh, + }, + }, + ]; + + const createSESButton = + Create SES sender + ; + + const tableComponent = No SES senders to display} + body="Set up an outbound email server by creating a sender. You will select a sender when configuring email channels." + actions={ + Create SES sender + } />} + onChange={this.onTableChange} + pagination={pagination} + sorting={sorting} + loading={this.state.loading} + tableLayout="auto" />; + + const searchComponent = ; + return ( - - {({ onShow }) => ( + <> + {getUseUpdatedUx() ? ( + + Create SES sender + + ), + }, + ]} + /> + } + bodyStyles={{ padding: 'initial' }} + title="SES senders" + titleSize="m" + total={this.state.total} + > + + + {searchComponent} + + + + Actions + + } + isOpen={this.state.isPopoverOpen} + closePopover={() => this.setState({ isPopoverOpen: false })} + > + {actions.map((action) => ( + + {({ onShow }) => ( + { + this.setState({ isPopoverOpen: false }); + if (action.modal) { + onShow(action.modal, { + ...(action.modalParams || {}), + }); + } else if (action.action) { + action.action(); + } + }} + > + {action.label} + + )} + + ))} + + + + + {tableComponent} + + ) : ( + + {({ onShow }) => ( + + onShow(DeleteSenderModal, { + senders: this.state.selectedItems, + refresh: this.refresh, + }) + } + > + Delete + + )} + + ), + }, + { + component: ( - onShow(DeleteSenderModal, { - senders: this.state.selectedItems, - refresh: this.refresh, - }) + location.assign( + `#${ROUTES.EDIT_SES_SENDER}/${this.state.selectedItems[0]?.config_id}` + ) } > - Delete + Edit - )} - - ), - }, - { - component: ( - - location.assign( - `#${ROUTES.EDIT_SES_SENDER}/${this.state.selectedItems[0]?.config_id}` - ) - } - > - Edit - - ), - }, - { - component: ( - - Create SES sender - - ), - }, - ]} - /> - } - bodyStyles={{ padding: 'initial' }} - title="SES senders" - titleSize="m" - total={this.state.total} - > - - + ), + }, + { + component: ( + createSESButton + ), + }, + ]} + /> + } + bodyStyles={{ padding: 'initial' }} + title="SES senders" + titleSize="m" + total={this.state.total} + > + {searchComponent} + + {tableComponent} + - No SES senders to display} - body="Set up an outbound email server by creating a sender. You will select a sender when configuring email channels." - actions={ - - Create SES sender - - } - /> - } - onChange={this.onTableChange} - pagination={pagination} - sorting={sorting} - loading={this.state.loading} - tableLayout="auto" - /> - + )} + ); + } -} +}; diff --git a/public/pages/Emails/components/tables/SendersTable.tsx b/public/pages/Emails/components/tables/SendersTable.tsx index 663126bd..efee0179 100644 --- a/public/pages/Emails/components/tables/SendersTable.tsx +++ b/public/pages/Emails/components/tables/SendersTable.tsx @@ -6,8 +6,12 @@ import { EuiBasicTable, EuiSmallButton, + EuiContextMenuItem, EuiEmptyPrompt, + EuiFlexGroup, + EuiFlexItem, EuiHorizontalRule, + EuiPopover, EuiTableFieldDataColumnType, EuiTableSortingType, SortDirection, @@ -36,6 +40,7 @@ import { isDataSourceError, isDataSourceChanged, } from '../../../../components/MDSEnabledComponent/MDSEnabledComponent'; +import { getUseUpdatedUx } from '../../../../../public/services/utils/constants'; interface SendersTableProps { coreContext: CoreStart; @@ -69,6 +74,7 @@ export class SendersTable extends Component< filters: { encryptionMethod: [], }, + isPopoverOpen: false, // Initialize popover state }; this.columns = [ @@ -181,6 +187,12 @@ export class SendersTable extends Component< this.setState({ from: 0, search }); }; + togglePopover = () => { + this.setState((prevState) => ({ + isPopoverOpen: !prevState.isPopoverOpen, + })); + }; + render() { const page = Math.floor(this.state.from / this.state.size); @@ -203,91 +215,180 @@ export class SendersTable extends Component< onSelectionChange: this.onSelectionChange, }; + const actions = [ + { + label: 'Edit', + disabled: this.state.selectedItems.length !== 1, + action: () => { + location.assign(`#${ROUTES.EDIT_SENDER}/${this.state.selectedItems[0]?.config_id}`); + }, + }, + { + label: 'Delete', + disabled: this.state.selectedItems.length === 0, + modal: DeleteSenderModal, + modalParams: { + senders: this.state.selectedItems, + refresh: this.refresh, + }, + }, + ]; + + const createSMTPButton = + Create SMTP sender + ; + + const senderControlComponent = this.setState({ filters })} />; + + const basicTableComponent = No SMTP senders to display} + body="Set up an outbound email server by creating a sender. You will select a sender when configuring email channels." + actions={ + Create SMTP sender + } />} + onChange={this.onTableChange} + pagination={pagination} + sorting={sorting} + loading={this.state.loading} />; + return ( - - {({ onShow }) => ( + <> + {getUseUpdatedUx() ? ( + + Create SMTP sender + + ), + }, + ]} + /> + } + bodyStyles={{ padding: 'initial' }} + title="SMTP senders" + titleSize="m" + total={this.state.total} + > + + + {senderControlComponent} + + + + Actions + + } + isOpen={this.state.isPopoverOpen} + closePopover={() => this.setState({ isPopoverOpen: false })} + > + {actions.map((action) => ( + + {({ onShow }) => ( + { + this.setState({ isPopoverOpen: false }); + if (action.modal) { + onShow(action.modal, { + ...(action.modalParams || {}), + }); + } else if (action.action) { + action.action(); + } + }} + > + {action.label} + + )} + + ))} + + + + + {basicTableComponent} + + ) : ( + + {({ onShow }) => ( + + onShow(DeleteSenderModal, { + senders: this.state.selectedItems, + refresh: this.refresh, + }) + } + > + Delete + + )} + + ), + }, + { + component: ( - onShow(DeleteSenderModal, { - senders: this.state.selectedItems, - refresh: this.refresh, - }) + location.assign( + `#${ROUTES.EDIT_SENDER}/${this.state.selectedItems[0]?.config_id}` + ) } > - Delete + Edit - )} - - ), - }, - { - component: ( - - location.assign( - `#${ROUTES.EDIT_SENDER}/${this.state.selectedItems[0]?.config_id}` - ) - } - > - Edit - - ), - }, - { - component: ( - - Create SMTP sender - - ), - }, - ]} - /> - } - bodyStyles={{ padding: 'initial' }} - title="SMTP senders" - titleSize="m" - total={this.state.total} - > - this.setState({ filters })} - /> - - - No SMTP senders to display} - body="Set up an outbound email server by creating a sender. You will select a sender when configuring email channels." - actions={ - - Create SMTP sender - - } - /> - } - onChange={this.onTableChange} - pagination={pagination} - sorting={sorting} - loading={this.state.loading} - /> - + ), + }, + { + component: ( + createSMTPButton + ), + }, + ]} + /> + } + bodyStyles={{ padding: 'initial' }} + title="SMTP senders" + titleSize="m" + total={this.state.total} + > + {senderControlComponent} + + {basicTableComponent} + + )} + ); } -} +}; diff --git a/public/pages/Main/Main.tsx b/public/pages/Main/Main.tsx index d2e7c083..768b86c2 100644 --- a/public/pages/Main/Main.tsx +++ b/public/pages/Main/Main.tsx @@ -6,7 +6,7 @@ import { EuiPage, EuiPageBody, EuiPageSideBar, EuiSideNav } from '@elastic/eui'; import React, { Component, createContext, useContext } from 'react'; import { Redirect, Route, RouteComponentProps, Switch } from 'react-router-dom'; -import { CoreStart, SavedObject } from '../../../../../src/core/public'; +import { CoreStart, MountPoint, SavedObject } from '../../../../../src/core/public'; import { CoreServicesConsumer, CoreServicesContext } from '../../components/coreServices'; import { ModalProvider, ModalRoot } from '../../components/Modal'; import { BrowserServices } from '../../models/interfaces'; @@ -28,13 +28,14 @@ import { DataSourceSelectableConfig, DataSourceViewConfig, } from "../../../../../src/plugins/data_source_management/public"; -import { DataSourceOption } from "../../../../../src/plugins/data_source_management/public/components/data_source_menu/types"; +import { DataSourceMenuProps, DataSourceOption } from "../../../../../src/plugins/data_source_management/public/components/data_source_menu/types"; import _ from "lodash"; import { NotificationService } from '../../services'; import { HttpSetup } from '../../../../../src/core/public'; import * as pluginManifest from "../../../opensearch_dashboards.json"; import { DataSourceAttributes } from "../../../../../src/plugins/data_source/common/data_sources"; import semver from "semver"; +import { ComponentType } from 'react'; enum Navigation { Notifications = 'Notifications', @@ -100,7 +101,7 @@ export default class Main extends Component { this.setServerFeatures(); } - componentDidUpdate(prevProps, prevState) { + componentDidUpdate(prevProps: any, prevState: { dataSourceId: string; }) { if (this.props.multiDataSourceEnabled && (prevState.dataSourceId !== this.state.dataSourceId)) { // Call setServerFeatures when dataSourceId is updated or dataSourceComponent is loaded this.setServerFeatures(); @@ -190,7 +191,7 @@ export default class Main extends Component { const { location: { pathname }, } = this.props; - let DataSourceMenuSelectable, DataSourceMenuView; + let DataSourceMenuSelectable: React.JSX.IntrinsicAttributes | ComponentType>, DataSourceMenuView: React.JSX.IntrinsicAttributes | ComponentType>; let activeOption: DataSourceOption[] | undefined; if (this.props.multiDataSourceEnabled) { DataSourceMenuSelectable = this.props.dataSourceManagement?.ui?.getDataSourceMenu(); @@ -204,6 +205,7 @@ export default class Main extends Component { }, ]; } + const sideNav = [ { name: Navigation.Notifications, @@ -231,234 +233,250 @@ export default class Main extends Component { ], }, ]; + return ( - {(core: CoreStart | null) => + {(core: CoreStart | null) => ( core && ( - {(services: BrowserServices | null) => + {(services: BrowserServices | null) => ( services && ( - - {this.props.multiDataSourceEnabled && DataSourceMenuView && DataSourceMenuSelectable && ( - - ( - - )} - /> - ( - - )} - /> - - this.state.dataSourceReadOnly ? ( + + {this.props.multiDataSourceEnabled && DataSourceMenuView && DataSourceMenuSelectable && ( + + ( - ) : ( + )} + /> + ( - ) - } - /> - - )} - - {!this.state.dataSourceLoading && ( - <> - - {pathname !== ROUTES.CREATE_CHANNEL && - !pathname.startsWith(ROUTES.EDIT_CHANNEL) && - !pathname.startsWith(ROUTES.CHANNEL_DETAILS) && - pathname !== ROUTES.CREATE_SENDER && - !pathname.startsWith(ROUTES.EDIT_SENDER) && - pathname !== ROUTES.CREATE_SES_SENDER && - !pathname.startsWith(ROUTES.EDIT_SES_SENDER) && - pathname !== ROUTES.CREATE_RECIPIENT_GROUP && - !pathname.startsWith(ROUTES.EDIT_RECIPIENT_GROUP) && - // Conditionally render sidebar based on the feature flag - !core.chrome?.navGroup?.getNavGroupEnabled() && ( - - - )} - - - ( - - )} - /> - - ) => } - /> - - ) => } - /> - ( - - )} - /> - ( - // send dataSourceId as props or externally - )} - /> - ( - - )} - /> - ( - - )} - /> - ( - - )} - /> - ( - - )} - /> - ( - - )} - /> - ( - - )} - /> - ( - - )} - /> - - - + /> + + this.state.dataSourceReadOnly ? ( + + ) : ( + + ) + } + /> + )} - - + + {!this.state.dataSourceLoading && ( + <> + + {pathname !== ROUTES.CREATE_CHANNEL && + !pathname.startsWith(ROUTES.EDIT_CHANNEL) && + !pathname.startsWith(ROUTES.CHANNEL_DETAILS) && + pathname !== ROUTES.CREATE_SENDER && + !pathname.startsWith(ROUTES.EDIT_SENDER) && + pathname !== ROUTES.CREATE_SES_SENDER && + !pathname.startsWith(ROUTES.EDIT_SES_SENDER) && + pathname !== ROUTES.CREATE_RECIPIENT_GROUP && + !pathname.startsWith(ROUTES.EDIT_RECIPIENT_GROUP) && + !core.chrome?.navGroup?.getNavGroupEnabled() && ( + + + + )} + + + ( + + )} + /> + ) => ( + + )} + /> + ) => ( + + )} + /> + ( + + )} + /> + ( + + )} + /> + ( + + )} + /> + ( + + )} + /> + ( + + )} + /> + ( + + )} + /> + ( + + )} + /> + ( + + )} + /> + ( + + )} + /> + + + + + )} + + ) - } + )} ) - } + )} ); - } + }; } \ No newline at end of file diff --git a/public/pages/Main/__tests__/Main.test.tsx b/public/pages/Main/__tests__/Main.test.tsx index 6991045b..bc6ea556 100644 --- a/public/pages/Main/__tests__/Main.test.tsx +++ b/public/pages/Main/__tests__/Main.test.tsx @@ -18,9 +18,13 @@ import { notificationServiceMock, } from '../../../../test/mocks/serviceMock'; import { CoreServicesContext } from '../../../components/coreServices'; -import { ServicesContext } from '../../../services'; import { ROUTES } from '../../../utils/constants'; import httpClientMock from '../../../../test/mocks/httpClientMock'; +import { setupCoreStart } from '../../../../test/utils/helpers'; + +beforeAll(() => { + setupCoreStart(); +}); describe('
spec', () => { configure({ adapter: new Adapter() }); diff --git a/public/plugin.ts b/public/plugin.ts index f28c6710..cfc225bf 100644 --- a/public/plugin.ts +++ b/public/plugin.ts @@ -14,12 +14,14 @@ import { WorkspaceAvailability } from '../../../src/core/public'; import { + AppPluginStartDependencies, notificationsDashboardsPluginSetup, notificationsDashboardsPluginStart, NotificationsDashboardsSetupDeps, } from './types'; import { PLUGIN_NAME } from '../common'; import { ROUTES } from './utils/constants'; +import { setApplication, setBreadCrumbsSetter, setNavigationUI, setUISettings } from './services/utils/constants'; export class notificationsDashboardsPlugin implements @@ -135,7 +137,14 @@ export class notificationsDashboardsPlugin return {}; } - public start(core: CoreStart): notificationsDashboardsPluginStart { + public start( + core: CoreStart, + { navigation }: AppPluginStartDependencies + ): notificationsDashboardsPluginStart { + setUISettings(core.uiSettings); + setNavigationUI(navigation.ui); + setApplication(core.application); + setBreadCrumbsSetter(core.chrome.setBreadcrumbs); return {}; } diff --git a/public/services/utils/constants.ts b/public/services/utils/constants.ts new file mode 100644 index 00000000..18060c0b --- /dev/null +++ b/public/services/utils/constants.ts @@ -0,0 +1,28 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + + +import { createGetterSetter } from '../../../../../src/plugins/opensearch_dashboards_utils/common'; +import { CoreStart, IUiSettingsClient } from 'opensearch-dashboards/public'; +import { NavigationPublicPluginStart } from '../../../../../src/plugins/navigation/public'; + + +export const [getUISettings, setUISettings] = createGetterSetter('UISettings'); + +export const getUseUpdatedUx = () => { + return getUISettings().get('home:useNewHomePage', false); +}; + +export const [getNavigationUI, setNavigationUI] = createGetterSetter< + NavigationPublicPluginStart['ui'] +>('navigation'); + +export const [getApplication, setApplication] = createGetterSetter( + 'application' +); + +export const [getBreadCrumbsSetter, setBreadCrumbsSetter] = createGetterSetter< + CoreStart['chrome']['setBreadcrumbs'] +>('breadCrumbSetter'); \ No newline at end of file diff --git a/public/utils/constants.ts b/public/utils/constants.ts index c130f423..e934f4d6 100644 --- a/public/utils/constants.ts +++ b/public/utils/constants.ts @@ -3,6 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { ChromeBreadcrumb } from 'opensearch-dashboards/public'; +import { getBreadCrumbsSetter, getUseUpdatedUx } from '../services/utils/constants'; + export const DOCUMENTATION_LINK = ''; export const ALERTING_DOCUMENTATION_LINK = 'https://opensearch.org/docs/monitoring-plugins/alerting/monitors/#authenticate-sender-account'; @@ -24,7 +27,7 @@ export const ROUTES = Object.freeze({ }); export const BREADCRUMBS = Object.freeze({ - NOTIFICATIONS: { text: 'Notifications', href: '#/' }, + NOTIFICATIONS: { text: 'Notification channels', href: '#/' }, CHANNELS: { text: 'Channels', href: `#${ROUTES.CHANNELS}` }, CHANNEL_DETAILS: { text: 'Channels', href: `#${ROUTES.CHANNEL_DETAILS}` }, CREATE_CHANNEL: { text: 'Create channel', href: `#${ROUTES.CREATE_CHANNEL}` }, @@ -65,3 +68,7 @@ export const CUSTOM_WEBHOOK_ENDPOINT_TYPE = Object.freeze({ WEBHOOK_URL: 'Webhook URL', CUSTOM_URL: 'Custom attributes URL', }); + +export function setBreadcrumbs(crumbs: ChromeBreadcrumb[]) { + getBreadCrumbsSetter()(getUseUpdatedUx() ? crumbs : [BREADCRUMBS.NOTIFICATIONS, ...crumbs]); +} diff --git a/test/setup.jest.ts b/test/setup.jest.ts index e7c006b6..ad09db0d 100644 --- a/test/setup.jest.ts +++ b/test/setup.jest.ts @@ -2,8 +2,6 @@ * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ - -// import '@testing-library/jest-dom/extend-expect'; import { configure } from '@testing-library/react'; configure({ testIdAttribute: 'data-test-subj' }); diff --git a/test/utils/helpers.ts b/test/utils/helpers.ts new file mode 100644 index 00000000..cebea162 --- /dev/null +++ b/test/utils/helpers.ts @@ -0,0 +1,21 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { navigationPluginMock } from '../../../../src/plugins/navigation/public/mocks'; +import { applicationServiceMock } from '../../../../src/core/public/application/application_service.mock'; +import { uiSettingsServiceMock } from '../../../../src/core/public/ui_settings/ui_settings_service.mock'; +import { + setApplication, + setBreadCrumbsSetter, + setNavigationUI, + setUISettings, +} from '../../public/services/utils/constants'; + +export function setupCoreStart() { + setNavigationUI(navigationPluginMock.createStartContract().ui); + setApplication(applicationServiceMock.createStartContract()); + setUISettings(uiSettingsServiceMock.createStartContract()); + setBreadCrumbsSetter(jest.fn()); +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 72e0b934..f3bd84ef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -62,11 +62,11 @@ "@types/react" "^16" "@types/node@*": - version "20.12.7" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.7.tgz#04080362fa3dd6c5822061aa3124f5c152cff384" - integrity sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg== + version "22.3.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.3.0.tgz#7f8da0e2b72c27c4f9bd3cb5ef805209d04d4f9e" + integrity sha512-nrWpWVaDZuaVc5X84xJ0vNrLvomM205oQyLsRt7OHNZbSHslcWsvgFR7O7hire2ZonjLrWBbedmotmIlJDVd6g== dependencies: - undici-types "~5.26.4" + undici-types "~6.18.2" "@types/node@^14.14.31": version "14.18.63" @@ -244,9 +244,9 @@ aws-sign2@~0.7.0: integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== aws4@^1.8.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3" - integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg== + version "1.13.1" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.1.tgz#bb5f8b8a20739f6ae1caeaf7eea2c7913df8048e" + integrity sha512-u5w79Rd7SU4JaIlA/zFqG+gOiuq25q5VLyZ8E+ijJeILuTxVzZgp2CaGw/UTw6pXYN9XMO9yiqj/nEHmhTG5CA== base64-js@^1.3.1: version "1.5.1" @@ -335,9 +335,9 @@ cli-cursor@^3.1.0: restore-cursor "^3.1.0" cli-table3@~0.6.1: - version "0.6.4" - resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.4.tgz#d1c536b8a3f2e7bec58f67ac9e5769b1b30088b0" - integrity sha512-Lm3L0p+/npIQWNIiyF/nAn7T5dnOwR3xNTHXYEBFBFVPXzCVNZ5lqEC/1eo/EVfpDsQ1I+TX4ORPQgp+UI0CRw== + version "0.6.5" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.5.tgz#013b91351762739c16a9567c21a04632e449bf2f" + integrity sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ== dependencies: string-width "^4.2.0" optionalDependencies: @@ -487,9 +487,9 @@ data-view-byte-offset@^1.0.0: is-data-view "^1.0.1" dayjs@^1.10.4: - version "1.11.10" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0" - integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ== + version "1.11.12" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.12.tgz#5245226cc7f40a15bf52e0b99fd2a04669ccac1d" + integrity sha512-Rt2g+nTbLlDWZTwwrIXjy9MeiZmSDI375FvZs72ngxx8PDC6YXOeR3q5LAuPzjZQxhiWdRKac7RKV+YyQYfYIg== debug@^3.1.0: version "3.2.7" @@ -499,9 +499,9 @@ debug@^3.1.0: ms "^2.1.1" debug@^4.1.1, debug@^4.3.2: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + version "4.3.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" + integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg== dependencies: ms "2.1.2" @@ -514,7 +514,7 @@ define-data-property@^1.0.1, define-data-property@^1.1.4: es-errors "^1.3.0" gopd "^1.0.1" -define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: +define-properties@^1.2.0, define-properties@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== @@ -663,7 +663,7 @@ es-object-atoms@^1.0.0: dependencies: es-errors "^1.3.0" -es-set-tostringtag@^2.0.3: +es-set-tostringtag@^2.0.1, es-set-tostringtag@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== @@ -860,11 +860,12 @@ global-dirs@^3.0.0: ini "2.0.0" globalthis@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" - integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + version "1.0.4" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" + integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== dependencies: - define-properties "^1.1.3" + define-properties "^1.2.1" + gopd "^1.0.1" gopd@^1.0.1: version "1.0.1" @@ -912,11 +913,6 @@ has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: dependencies: has-symbols "^1.0.3" -has@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.4.tgz#2eb2860e000011dae4f1406a86fe80e530fb2ec6" - integrity sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ== - hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" @@ -1212,13 +1208,6 @@ loose-envify@^1.1.0, loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -1269,9 +1258,9 @@ object-assign@^4.1.1: integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== object-inspect@^1.13.1: - version "1.13.1" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" - integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + version "1.13.2" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" + integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== object-is@^1.1.2, object-is@^1.1.5: version "1.1.6" @@ -1381,13 +1370,16 @@ pretty-bytes@^5.6.0: integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== prop-types-exact@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/prop-types-exact/-/prop-types-exact-1.2.0.tgz#825d6be46094663848237e3925a98c6e944e9869" - integrity sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA== + version "1.2.5" + resolved "https://registry.yarnpkg.com/prop-types-exact/-/prop-types-exact-1.2.5.tgz#f275e7dc0d629c2f7414782e8189b3e2d2e9e158" + integrity sha512-wHDhA5TSSvU07gdzsdeT/FZg6zay94K4Y7swSK4YsRG3moWB0Qsp9g1Y5BBausP1HF8K4UeVe2Xt7ZFJByKp6A== dependencies: - has "^1.0.3" - object.assign "^4.1.0" - reflect.ownkeys "^0.2.0" + call-bind "^1.0.7" + es-errors "^1.3.0" + hasown "^2.0.2" + isarray "^2.0.5" + object.assign "^4.1.5" + reflect.ownkeys "^1.1.4" prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" @@ -1448,10 +1440,16 @@ react-test-renderer@^16.0.0-0: react-is "^16.8.6" scheduler "^0.19.1" -reflect.ownkeys@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460" - integrity sha512-qOLsBKHCpSOFKK1NUOCGC5VyeufB6lEsFe92AL2bhIJsacZS1qdoOZSbPk3MYKuT2cFlRDnulKXuuElIrMjGUg== +reflect.ownkeys@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-1.1.4.tgz#3cf21da448f2aff8aba63ca601f65c99482e692c" + integrity sha512-iUNmtLgzudssL+qnTUosCmnq3eczlrVd1wXrgx/GhiI/8FvwrTYWtCJ9PNvWIRX+4ftupj2WUfB5mu5s9t6LnA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-set-tostringtag "^2.0.1" + globalthis "^1.0.3" regexp.prototype.flags@^1.5.2: version "1.5.2" @@ -1484,9 +1482,9 @@ restore-cursor@^3.1.0: signal-exit "^3.0.2" rfdc@^1.3.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.1.tgz#2b6d4df52dffe8bb346992a10ea9451f24373a8f" - integrity sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg== + version "1.4.1" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== rxjs@^7.5.1: version "7.8.1" @@ -1543,11 +1541,9 @@ semver@^6.3.1: integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== semver@^7.3.2: - version "7.6.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" - integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== - dependencies: - lru-cache "^6.0.0" + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== set-function-length@^1.2.1: version "1.2.2" @@ -1710,9 +1706,9 @@ tmp@~0.2.1: integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w== tough-cookie@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" - integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== + version "4.1.4" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36" + integrity sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag== dependencies: psl "^1.1.33" punycode "^2.1.1" @@ -1720,9 +1716,9 @@ tough-cookie@^4.1.3: url-parse "^1.5.3" tslib@^2.1.0: - version "2.6.2" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" - integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + version "2.6.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" + integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== tunnel-agent@^0.6.0: version "0.6.0" @@ -1795,10 +1791,10 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" -undici-types@~5.26.4: - version "5.26.5" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" - integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== +undici-types@~6.18.2: + version "6.18.2" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.18.2.tgz#8b678cf939d4fc9ec56be3c68ed69c619dee28b0" + integrity sha512-5ruQbENj95yDYJNS3TvcaxPMshV7aizdv/hWYjGIKoANWKjhWNBsr2YEuYZKodQulB1b8l7ILOuDQep3afowQQ== universalify@^0.2.0: version "0.2.0" @@ -1837,6 +1833,11 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +web-streams-polyfill@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0.tgz#74cedf168339ee6e709532f76c49313a8c7acdac" + integrity sha512-0zJXHRAYEjM2tUfZ2DiSOHAa2aw1tisnnhU3ufD57R8iefL+DcdJyRBRyJpG+NUimDgbTI/lH+gAE1PAvV3Cgw== + which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" @@ -1889,11 +1890,6 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - yauzl@^2.10.0: version "2.10.0" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"