From 37b6cbb589d10d24caba6e3f066d8f3a188883a3 Mon Sep 17 00:00:00 2001 From: Riya <69919272+riysaxen-amzn@users.noreply.github.com> Date: Wed, 24 Jul 2024 09:36:47 -0700 Subject: [PATCH 01/21] added release notes 2.16 (#227) * added release notes 2.16 Signed-off-by: Riya Saxena * added release notes for 2.16 Signed-off-by: Riya Saxena --------- Signed-off-by: Riya Saxena --- ...shboards-notifications.release-notes-2.16.0.0.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 release-notes/opensearch-dashboards-notifications.release-notes-2.16.0.0.md diff --git a/release-notes/opensearch-dashboards-notifications.release-notes-2.16.0.0.md b/release-notes/opensearch-dashboards-notifications.release-notes-2.16.0.0.md new file mode 100644 index 00000000..4a349bbb --- /dev/null +++ b/release-notes/opensearch-dashboards-notifications.release-notes-2.16.0.0.md @@ -0,0 +1,13 @@ +## Version 2.16.0.0 2024-07-23 +Compatible with OpenSearch Dashboards 2.16.0 + +### Maintenance +* Increment version to 2.16.0.0 ([#216](https://github.com/opensearch-project/dashboards-notifications/pull/216)) +* Increment version to 2.16.0.0 ([#224](https://github.com/opensearch-project/dashboards-notifications/pull/224)) + +### Features/Enhancements +* side navigation changes for notifications ([#222](https://github.com/opensearch-project/dashboards-notifications/pull/222))([#225](https://github.com/opensearch-project/dashboards-notifications/pull/225)) +* Backport/backporting to 2.x ([#223](https://github.com/opensearch-project/dashboards-notifications/pull/223)) + +### Documentation +* 2.16 release notes. ([#227](https://github.com/opensearch-project/dashboards-notifications/pull/227)) From 17976f75e14c6d78d9e086460ed61c40a4fd2d87 Mon Sep 17 00:00:00 2001 From: Riya Saxena Date: Sun, 11 Aug 2024 00:56:32 -0700 Subject: [PATCH 02/21] chanels complete Signed-off-by: Riya Saxena --- public/application.tsx | 1 + public/pages/Channels/Channels.tsx | 62 ++- .../components/ChannelActionsHeader.tsx | 121 +++++ .../components/details/ChannelDetails.tsx | 195 +++++--- public/pages/CreateChannel/CreateChannel.tsx | 40 +- public/pages/Main/Main.tsx | 450 ++++++++++-------- public/services/NavigationContext.ts | 19 + public/utils/constants.ts | 4 +- 8 files changed, 608 insertions(+), 284 deletions(-) create mode 100644 public/pages/Channels/components/ChannelActionsHeader.tsx create mode 100644 public/services/NavigationContext.ts diff --git a/public/application.tsx b/public/application.tsx index 11b72d82..b4bef5c5 100644 --- a/public/application.tsx +++ b/public/application.tsx @@ -31,6 +31,7 @@ export const renderApp = ( dataSourceManagement={dataSourceManagement} http={coreStart.http} // Pass http as a prop defaultRoute={defaultRoute} + navigation={pluginStartDependencies.navigation} /> )} diff --git a/public/pages/Channels/Channels.tsx b/public/pages/Channels/Channels.tsx index f472761a..8e89588f 100644 --- a/public/pages/Channels/Channels.tsx +++ b/public/pages/Channels/Channels.tsx @@ -43,9 +43,14 @@ import MDSEnabledComponent, { isDataSourceChanged, isDataSourceError, } from '../../components/MDSEnabledComponent/MDSEnabledComponent'; +import { NavigationPublicPluginStart, TopNavControlButtonData } from 'src/plugins/navigation/public'; +import { ApplicationStart } from 'opensearch-dashboards/public'; interface ChannelsProps extends RouteComponentProps, DataSourceMenuProperties { notificationService: NotificationService; + navigationUI: NavigationPublicPluginStart['ui']; + showActionsInHeader: boolean; + application: ApplicationStart; } interface ChannelsState extends TableState, DataSourceMenuProperties { @@ -215,6 +220,33 @@ export class Channels extends MDSEnabledComponent onSelectionChange: this.onSelectionChange, }; + const { HeaderControl } = this.props.navigationUI; + const showActionsInHeader = this.props.showActionsInHeader; + const { setAppRightControls } = this.props.application; + + const headerControls = [ + { + renderComponent: ( + this.setState({ selectedItems })} + items={this.state.items} + setItems={(items) => this.setState({ items })} + refresh={this.refresh} + /> + ), + }, + { + id: 'Create Channel', + label: 'Create channel', + iconType: 'plus', + fill: true, + href: `#${ROUTES.CREATE_CHANNEL}`, + testId: 'createButton', + controlType: 'button', + }, + ]; + return ( <> + ) : ( - this.setState({ selectedItems }) - } + setSelected={(selectedItems) => this.setState({ selectedItems })} items={this.state.items} - setItems={(items: ChannelItemType[]) => - this.setState({ items }) - } + setItems={(items) => this.setState({ items })} refresh={this.refresh} /> ), }, { - component: ( + component: !showActionsInHeader ? ( Create channel - ), + ) : null, }, ]} /> } - bodyStyles={{ padding: 'initial' }} - title="Channels" - titleSize="m" - total={this.state.total} > onFiltersChange={this.onFiltersChange} /> - void; +} + +interface ChannelActionsProps { + selected: ChannelItemType[]; + setSelected: (items: ChannelItemType[]) => void; + items: ChannelItemType[]; + setItems: (items: ChannelItemType[]) => void; + refresh: () => void; +} + +export function ChannelActionsHeader(props: ChannelActionsProps) { + const coreContext = useContext(CoreServicesContext)!; + const servicesContext = useContext(ServicesContext)!; + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + const actions: ChannelActionsParams[] = [ + { + label: 'Edit', + disabled: props.selected.length !== 1, + href: `#${ROUTES.EDIT_CHANNEL}/${props.selected[0]?.config_id}`, + }, + { + label: 'Delete', + disabled: props.selected.length === 0, + modal: DeleteChannelModal, + modalParams: { refresh: props.refresh }, + }, + { + label: 'Mute', + disabled: props.selected.length !== 1 || !props.selected[0].is_enabled, + modal: MuteChannelModal, + modalParams: { refresh: props.refresh, setSelected: props.setSelected }, + }, + { + label: 'Unmute', + disabled: props.selected.length !== 1 || props.selected[0].is_enabled, + action: async () => { + const channel = { ...props.selected[0], is_enabled: true }; + servicesContext.notificationService + .updateConfig(channel.config_id, channel) + .then((resp) => { + coreContext.notifications.toasts.addSuccess( + `Channel ${channel.name} successfully unmuted.` + ); + props.setSelected([channel]); + setTimeout(() => props.refresh(), SERVER_DELAY); + }) + .catch((error) => { + coreContext.notifications.toasts.addError(error?.body || error, { + title: 'Failed to unmute channel', + }); + }); + }, + }, + ]; + + return ( + + {({ onShow }) => ( + setIsPopoverOpen(!isPopoverOpen)} + > + Actions + + } + isOpen={isPopoverOpen} + closePopover={() => setIsPopoverOpen(false)} + > + {actions.map((params) => ( + { + setIsPopoverOpen(false); + if (params.modal) { + onShow(params.modal, { + selected: props.selected, + ...(params.modalParams || {}), + }); + } + if (params.href) location.assign(params.href); + if (params.action) params.action(); + }} + > + {params.label} + + ))} + + )} + + ); +} diff --git a/public/pages/Channels/components/details/ChannelDetails.tsx b/public/pages/Channels/components/details/ChannelDetails.tsx index e238cec0..dc1ba546 100644 --- a/public/pages/Channels/components/details/ChannelDetails.tsx +++ b/public/pages/Channels/components/details/ChannelDetails.tsx @@ -31,8 +31,17 @@ import { MuteChannelModal } from '../modals/MuteChannelModal'; import { ChannelDetailItems } from './ChannelDetailItems'; import { ChannelDetailsActions } from './ChannelDetailsActions'; import { ChannelSettingsDetails } from './ChannelSettingsDetails'; +import { NavigationPublicPlugin } from 'src/plugins/navigation/public/plugin'; +import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; +import { ApplicationStart } from 'opensearch-dashboards/public'; -interface ChannelDetailsProps extends RouteComponentProps<{ id: string }> {} +interface ChannelDetailsProps extends RouteComponentProps<{ + id: string +}> { + navigationUI: NavigationPublicPluginStart['ui']; + showActionsInHeader: boolean; + application: ApplicationStart; +} export function ChannelDetails(props: ChannelDetailsProps) { const coreContext = useContext(CoreServicesContext)!; @@ -41,6 +50,61 @@ export function ChannelDetails(props: ChannelDetailsProps) { const [channel, setChannel] = useState(); const [toasts, setToasts] = useState([]); + const ChannelHeaderContent = () => { + const coreContext = useContext(CoreServicesContext)!; + const servicesContext = useContext(ServicesContext)!; + + return ( + + + {channel?.is_enabled === undefined ? null : channel.is_enabled ? ( + Active + ) : ( + Muted + )} + + + + {channel && } + + + {channel && ( + + {({ onShow }) => ( + { + if (!channel) return; + if (channel.is_enabled) { + onShow(MuteChannelModal, { + selected: [channel], + setSelected: (selected) => 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'} + + )} + + )} + + + ); + }; + + useEffect(() => { coreContext.chrome.setBreadcrumbs([ BREADCRUMBS.NOTIFICATIONS, @@ -125,28 +189,36 @@ export function ChannelDetails(props: ChannelDetailsProps) { }, ]; + + const { HeaderControl } = props.navigationUI; + const showActionsInHeader = props.showActionsInHeader; + const { setAppRightControls } = props.application; + + return ( <> - { - setToasts(toasts.filter((toast) => toast.id !== removedToast.id)); - }} - toastLifeTimeMs={60000} - /> - - - + {showActionsInHeader ? ( + <> + + ), + }, + ]} + bodyStyles={{ padding: 'initial' }} + title="Channel Details" + titleSize="m" + /> + + ) : ( +
+ +

{channel?.name || '-'}

+
- - -

{channel?.name || '-'}

-
-
{channel?.is_enabled === undefined ? null : channel.is_enabled ? ( Active @@ -154,47 +226,46 @@ export function ChannelDetails(props: ChannelDetailsProps) { Muted )} -
- - - - {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'} - + + + {channel && } + + + {channel && ( + + {({ onShow }) => ( + { + if (!channel) return; + if (channel.is_enabled) { + onShow(MuteChannelModal, { + selected: [channel], + setSelected: (selected) => 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'} + + )} + )} - - )} - - + + +
+ )} + ); -} +}; + diff --git a/public/pages/CreateChannel/CreateChannel.tsx b/public/pages/CreateChannel/CreateChannel.tsx index f814794c..e9cceae9 100644 --- a/public/pages/CreateChannel/CreateChannel.tsx +++ b/public/pages/CreateChannel/CreateChannel.tsx @@ -29,7 +29,7 @@ import { CUSTOM_WEBHOOK_ENDPOINT_TYPE, ROUTES, } 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,9 +55,14 @@ import { validateRecipients, validateWebhookURL, } from './utils/validationHelper'; +import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; +import { ApplicationStart } from 'opensearch-dashboards/public'; interface CreateChannelsProps extends RouteComponentProps<{ id?: string }> { edit?: boolean; + navigationUI: NavigationPublicPluginStart['ui']; + showActionsInHeader: boolean; + application: ApplicationStart; } type InputErrorsType = { [key: string]: string[] }; @@ -142,6 +147,10 @@ export function CreateChannel(props: CreateChannelsProps) { roleArn: [], }); + const { HeaderControl } = props.navigationUI; + const showActionsInHeader = props.showActionsInHeader; + const { setAppDescriptionControls } = props.application; + useEffect(() => { coreContext.chrome.setBreadcrumbs([ BREADCRUMBS.NOTIFICATIONS, @@ -365,9 +374,16 @@ export function CreateChannel(props: CreateChannelsProps) { - -

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

-
+ {showActionsInHeader ? ( + + ) : ( + +

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

+
+ )} - ): channelType === BACKEND_CHANNEL_TYPE.EMAIL ? ( + ) : channelType === BACKEND_CHANNEL_TYPE.EMAIL ? ( { coreContext.notifications.toasts.addSuccess( - `Channel ${name} successfully ${ - props.edit ? 'updated' : 'created' + `Channel ${name} successfully ${props.edit ? 'updated' : 'created' }.` ); setTimeout(() => (location.hash = prevURL), SERVER_DELAY); @@ -514,9 +529,8 @@ export function CreateChannel(props: CreateChannelsProps) { coreContext.notifications.toasts.addError( error?.body || error, { - title: `Failed to ${ - props.edit ? 'update' : 'create' - } channel.`, + title: `Failed to ${props.edit ? 'update' : 'create' + } channel.`, } ); }); diff --git a/public/pages/Main/Main.tsx b/public/pages/Main/Main.tsx index d2e7c083..990aad82 100644 --- a/public/pages/Main/Main.tsx +++ b/public/pages/Main/Main.tsx @@ -35,6 +35,8 @@ 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 { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; +import { NavigationMenuContext } from '../../services/NavigationContext'; enum Navigation { Notifications = 'Notifications', @@ -52,6 +54,7 @@ interface MainProps extends RouteComponentProps { multiDataSourceEnabled: boolean; dataSourceManagement: DataSourceManagementPluginSetup; defaultRoute?: string; + navigation: NavigationPublicPluginStart; } export interface MainState extends Pick { @@ -204,6 +207,7 @@ export default class Main extends Component { }, ]; } + const sideNav = [ { name: Navigation.Notifications, @@ -231,234 +235,294 @@ 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/services/NavigationContext.ts b/public/services/NavigationContext.ts new file mode 100644 index 00000000..a3e16fb7 --- /dev/null +++ b/public/services/NavigationContext.ts @@ -0,0 +1,19 @@ +import { ApplicationStart } from "opensearch-dashboards/public"; +import { createContext } from "react"; +import { NavigationPublicPluginStart } from "src/plugins/navigation/public"; + +export interface NavigationMenuProperties { + navigationUI: NavigationPublicPluginStart['ui']; + showActionsInHeader: boolean; + application: ApplicationStart; +} + +const NavigationMenuContext = createContext({ + navigationUI: {} as NavigationPublicPluginStart['ui'], + showActionsInHeader: false, + application: {} as ApplicationStart, +}); + +const NavigationMenuConsumer = NavigationMenuContext.Consumer; + +export { NavigationMenuContext, NavigationMenuConsumer }; diff --git a/public/utils/constants.ts b/public/utils/constants.ts index c130f423..cf407306 100644 --- a/public/utils/constants.ts +++ b/public/utils/constants.ts @@ -24,10 +24,10 @@ 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}` }, + CREATE_CHANNEL: { text: '', href: `#${ROUTES.CREATE_CHANNEL}` }, EDIT_CHANNEL: { text: 'Edit channel' }, EMAIL_SENDERS: { text: 'Email senders', href: `#${ROUTES.EMAIL_SENDERS}` }, EMAIL_GROUPS: { text: 'Email recipient groups', href: `#${ROUTES.EMAIL_GROUPS}` }, From e55c9c56350a9c6e810fc3ee97ecb03fc5943308 Mon Sep 17 00:00:00 2001 From: Riya Saxena Date: Sun, 11 Aug 2024 18:54:21 -0700 Subject: [PATCH 03/21] almost done Signed-off-by: Riya Saxena --- public/pages/Channels/Channels.tsx | 2 +- .../components/details/ChannelDetails.tsx | 1 - public/pages/Emails/CreateRecipientGroup.tsx | 27 ++++++++------ public/pages/Emails/CreateSESSender.tsx | 13 +++++-- public/pages/Emails/CreateSender.tsx | 27 ++++++++------ public/pages/Emails/EmailGroups.tsx | 21 ++++++++--- public/pages/Emails/EmailSenders.tsx | 23 +++++++++--- .../tables/RecipientGroupsTable.tsx | 35 ++++++++++++++++--- public/pages/Main/Main.tsx | 26 +++++++------- 9 files changed, 120 insertions(+), 55 deletions(-) diff --git a/public/pages/Channels/Channels.tsx b/public/pages/Channels/Channels.tsx index 8e89588f..a9e7aef4 100644 --- a/public/pages/Channels/Channels.tsx +++ b/public/pages/Channels/Channels.tsx @@ -43,7 +43,7 @@ import MDSEnabledComponent, { isDataSourceChanged, isDataSourceError, } from '../../components/MDSEnabledComponent/MDSEnabledComponent'; -import { NavigationPublicPluginStart, TopNavControlButtonData } from 'src/plugins/navigation/public'; +import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; import { ApplicationStart } from 'opensearch-dashboards/public'; interface ChannelsProps extends RouteComponentProps, DataSourceMenuProperties { diff --git a/public/pages/Channels/components/details/ChannelDetails.tsx b/public/pages/Channels/components/details/ChannelDetails.tsx index dc1ba546..a209093e 100644 --- a/public/pages/Channels/components/details/ChannelDetails.tsx +++ b/public/pages/Channels/components/details/ChannelDetails.tsx @@ -31,7 +31,6 @@ import { MuteChannelModal } from '../modals/MuteChannelModal'; import { ChannelDetailItems } from './ChannelDetailItems'; import { ChannelDetailsActions } from './ChannelDetailsActions'; import { ChannelSettingsDetails } from './ChannelSettingsDetails'; -import { NavigationPublicPlugin } from 'src/plugins/navigation/public/plugin'; import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; import { ApplicationStart } from 'opensearch-dashboards/public'; diff --git a/public/pages/Emails/CreateRecipientGroup.tsx b/public/pages/Emails/CreateRecipientGroup.tsx index cf8386bf..d566f7df 100644 --- a/public/pages/Emails/CreateRecipientGroup.tsx +++ b/public/pages/Emails/CreateRecipientGroup.tsx @@ -26,10 +26,15 @@ import { validateRecipientGroupEmails, validateRecipientGroupName, } from './utils/validationHelper'; +import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; +import { ApplicationStart } from 'opensearch-dashboards/public'; interface CreateRecipientGroupProps extends RouteComponentProps<{ id?: string }> { edit?: boolean; + navigationUI: NavigationPublicPluginStart['ui']; + showActionsInHeader: boolean; + application: ApplicationStart; } export function CreateRecipientGroup(props: CreateRecipientGroupProps) { @@ -103,9 +108,11 @@ export function CreateRecipientGroup(props: CreateRecipientGroupProps) { return ( <> - -

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

-
+ {!props.showActionsInHeader && ( + +

{`${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 +180,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 61603309..42dbd9e7 100644 --- a/public/pages/Emails/CreateSESSender.tsx +++ b/public/pages/Emails/CreateSESSender.tsx @@ -28,9 +28,14 @@ import { validateRoleArn, validateSenderName, } from './utils/validationHelper'; +import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; +import { ApplicationStart } from 'opensearch-dashboards/public'; interface CreateSESSenderProps extends RouteComponentProps<{ id?: string }> { edit?: boolean; + navigationUI: NavigationPublicPluginStart['ui']; + showActionsInHeader: boolean; + application: ApplicationStart; } export function CreateSESSender(props: CreateSESSenderProps) { @@ -101,9 +106,11 @@ export function CreateSESSender(props: CreateSESSenderProps) { return ( <> - -

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

-
+ {!props.showActionsInHeader && ( + +

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

+
+ )} { edit?: boolean; + navigationUI: NavigationPublicPluginStart['ui']; + showActionsInHeader: boolean; + application: ApplicationStart; } export function CreateSender(props: CreateSenderProps) { @@ -98,9 +103,11 @@ export function CreateSender(props: CreateSenderProps) { return ( <> - -

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

-
+ {!props.showActionsInHeader && ( + +

{`${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 +178,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..7d223cb5 100644 --- a/public/pages/Emails/EmailGroups.tsx +++ b/public/pages/Emails/EmailGroups.tsx @@ -9,11 +9,15 @@ 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 { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; +import { ApplicationStart } from 'opensearch-dashboards/public'; interface EmailGroupsProps extends RouteComponentProps { notificationService: NotificationService; + navigationUI: NavigationPublicPluginStart['ui']; + showActionsInHeader: boolean; + application: ApplicationStart; } export function EmailGroups(props: EmailGroupsProps) { @@ -28,12 +32,19 @@ export function EmailGroups(props: EmailGroupsProps) { return ( <> - -

Email recipient groups

-
+ {!props.showActionsInHeader && ( + +

Email recipient groups

+
+ )} - + ); } diff --git a/public/pages/Emails/EmailSenders.tsx b/public/pages/Emails/EmailSenders.tsx index a93803b8..c83768cc 100644 --- a/public/pages/Emails/EmailSenders.tsx +++ b/public/pages/Emails/EmailSenders.tsx @@ -12,9 +12,14 @@ import { MainContext } from '../Main/Main'; import { SendersTable } from './components/tables/SendersTable'; import { SESSendersTable } from './components/tables/SESSendersTable'; import { NotificationService } from '../../services'; +import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; +import { ApplicationStart } from 'opensearch-dashboards/public'; interface EmailSendersProps extends RouteComponentProps { notificationService: NotificationService; + navigationUI: NavigationPublicPluginStart['ui']; + showActionsInHeader: boolean; + application: ApplicationStart; } export function EmailSenders(props: EmailSendersProps) { @@ -30,15 +35,21 @@ export function EmailSenders(props: EmailSendersProps) { window.scrollTo(0, 0); }, []); + const showActionsInHeader = props.showActionsInHeader; + return ( <> - -

Email senders

-
+ {!showActionsInHeader && ( + +

Email senders

+
+ )} {mainStateContext.availableConfigTypes.includes('smtp_account') && ( <> - + )} @@ -46,7 +57,9 @@ export function EmailSenders(props: EmailSendersProps) { {mainStateContext.availableConfigTypes.includes('ses_account') && ( <> - + )} diff --git a/public/pages/Emails/components/tables/RecipientGroupsTable.tsx b/public/pages/Emails/components/tables/RecipientGroupsTable.tsx index 50205e04..a29890c5 100644 --- a/public/pages/Emails/components/tables/RecipientGroupsTable.tsx +++ b/public/pages/Emails/components/tables/RecipientGroupsTable.tsx @@ -18,7 +18,7 @@ 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 } from 'react'; -import { CoreStart } from '../../../../../../../src/core/public'; +import { ApplicationStart, CoreStart } from '../../../../../../../src/core/public'; import { RecipientGroupItemType, TableState, @@ -38,10 +38,14 @@ import { isDataSourceError, isDataSourceChanged, } from '../../../../components/MDSEnabledComponent/MDSEnabledComponent'; +import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; interface RecipientGroupsTableProps { coreContext: CoreStart; notificationService: NotificationService; + navigationUI: NavigationPublicPluginStart['ui']; + showActionsInHeader: boolean; + application: ApplicationStart; } interface RecipientGroupsTableState @@ -218,6 +222,22 @@ export class RecipientGroupsTable extends Component< onSelectionChange: this.onSelectionChange, }; + const { HeaderControl } = this.props.navigationUI; + const showActionsInHeader = this.props.showActionsInHeader; + const { setAppRightControls } = this.props.application; + + const headerControls = [ + { + id: 'Create recipient group', + label: 'Create recipient group', + iconType: 'plus', + fill: true, + href: `#${ROUTES.CREATE_RECIPIENT_GROUP}`, + testId: 'createButton', + controlType: 'button', + }, + ]; + return ( <> + ) : ( - Create recipient group - + Create recipient group + ), - }, + } ]} /> } diff --git a/public/pages/Main/Main.tsx b/public/pages/Main/Main.tsx index 990aad82..d546bc01 100644 --- a/public/pages/Main/Main.tsx +++ b/public/pages/Main/Main.tsx @@ -372,7 +372,7 @@ export default class Main extends Component { render={(routeProps: RouteComponentProps) => ( @@ -384,7 +384,7 @@ export default class Main extends Component { @@ -395,7 +395,7 @@ export default class Main extends Component { render={(routeProps: RouteComponentProps<{ id: string }>) => ( @@ -407,7 +407,7 @@ export default class Main extends Component { @@ -419,7 +419,7 @@ export default class Main extends Component { @@ -431,7 +431,7 @@ export default class Main extends Component { @@ -442,7 +442,7 @@ export default class Main extends Component { render={(routeProps: RouteComponentProps) => ( @@ -454,7 +454,7 @@ export default class Main extends Component { @@ -465,7 +465,7 @@ export default class Main extends Component { render={(routeProps: RouteComponentProps) => ( @@ -477,7 +477,7 @@ export default class Main extends Component { @@ -488,7 +488,7 @@ export default class Main extends Component { render={(routeProps: RouteComponentProps) => ( @@ -500,13 +500,13 @@ export default class Main extends Component { )} /> - + From 9ad26d8445a9f8e53308802fe109dbd67e61c8ed Mon Sep 17 00:00:00 2001 From: Riya Saxena Date: Tue, 13 Aug 2024 10:36:18 -0700 Subject: [PATCH 04/21] corrected Chanel list page Signed-off-by: Riya Saxena --- public/pages/Channels/Channels.tsx | 75 ++++++---- .../components/ChannelActionsHeader.tsx | 38 +---- .../tables/RecipientGroupsTable.tsx | 134 +++++++++--------- public/pages/Main/Main.tsx | 5 +- 4 files changed, 121 insertions(+), 131 deletions(-) diff --git a/public/pages/Channels/Channels.tsx b/public/pages/Channels/Channels.tsx index a9e7aef4..663431f7 100644 --- a/public/pages/Channels/Channels.tsx +++ b/public/pages/Channels/Channels.tsx @@ -225,17 +225,6 @@ export class Channels extends MDSEnabledComponent const { setAppRightControls } = this.props.application; const headerControls = [ - { - renderComponent: ( - this.setState({ selectedItems })} - items={this.state.items} - setItems={(items) => this.setState({ items })} - refresh={this.refresh} - /> - ), - }, { id: 'Create Channel', label: 'Create channel', @@ -246,7 +235,7 @@ export class Channels extends MDSEnabledComponent controlType: 'button', }, ]; - + return ( <> titleSize="m" total={this.state.total} /> - ) : ( - this.setState({ selectedItems })} - items={this.state.items} - setItems={(items) => this.setState({ items })} - refresh={this.refresh} - /> - ), + ) : null, }, { - component: !showActionsInHeader ? ( + component: !showActionsInHeader && ( Create channel - ) : null, + ), }, ]} /> } > - +
+ {showActionsInHeader ? ( +
+ {/* Channel Controls with Status and Type dropdowns */} + + {/* Channel Actions next to Status and Type dropdowns */} + this.setState({ selectedItems })} + items={this.state.items} + setItems={(items) => this.setState({ items })} + refresh={this.refresh} + /> +
+ ) : ( +
+ {/* Channel Controls with Status and Type dropdowns */} + + + {/* Channel Actions next to Create Channel button */} + this.setState({ selectedItems })} + items={this.state.items} + setItems={(items) => this.setState({ items })} + refresh={this.refresh} + /> + + Create channel + +
+ )} +
); + + } } diff --git a/public/pages/Channels/components/ChannelActionsHeader.tsx b/public/pages/Channels/components/ChannelActionsHeader.tsx index 97716e56..fb956ac8 100644 --- a/public/pages/Channels/components/ChannelActionsHeader.tsx +++ b/public/pages/Channels/components/ChannelActionsHeader.tsx @@ -4,15 +4,11 @@ */ import { EuiButton, EuiContextMenuItem, EuiPopover } from '@elastic/eui'; -import React, { useContext, useState } from 'react'; -import { SERVER_DELAY } from '../../../../common'; +import React, { useState } from 'react'; import { ChannelItemType } from '../../../../models/interfaces'; -import { CoreServicesContext } from '../../../components/coreServices'; import { ModalConsumer } from '../../../components/Modal'; -import { ServicesContext } from '../../../services'; import { ROUTES } from '../../../utils/constants'; import { DeleteChannelModal } from './modals/DeleteChannelModal'; -import { MuteChannelModal } from './modals/MuteChannelModal'; interface ChannelActionsParams { label: string; @@ -29,18 +25,17 @@ interface ChannelActionsProps { items: ChannelItemType[]; setItems: (items: ChannelItemType[]) => void; refresh: () => void; + href?: string; } export function ChannelActionsHeader(props: ChannelActionsProps) { - const coreContext = useContext(CoreServicesContext)!; - const servicesContext = useContext(ServicesContext)!; const [isPopoverOpen, setIsPopoverOpen] = useState(false); const actions: ChannelActionsParams[] = [ { label: 'Edit', disabled: props.selected.length !== 1, - href: `#${ROUTES.EDIT_CHANNEL}/${props.selected[0]?.config_id}`, + href: props.href, }, { label: 'Delete', @@ -48,33 +43,6 @@ export function ChannelActionsHeader(props: ChannelActionsProps) { modal: DeleteChannelModal, modalParams: { refresh: props.refresh }, }, - { - label: 'Mute', - disabled: props.selected.length !== 1 || !props.selected[0].is_enabled, - modal: MuteChannelModal, - modalParams: { refresh: props.refresh, setSelected: props.setSelected }, - }, - { - label: 'Unmute', - disabled: props.selected.length !== 1 || props.selected[0].is_enabled, - action: async () => { - const channel = { ...props.selected[0], is_enabled: true }; - servicesContext.notificationService - .updateConfig(channel.config_id, channel) - .then((resp) => { - coreContext.notifications.toasts.addSuccess( - `Channel ${channel.name} successfully unmuted.` - ); - props.setSelected([channel]); - setTimeout(() => props.refresh(), SERVER_DELAY); - }) - .catch((error) => { - coreContext.notifications.toasts.addError(error?.body || error, { - title: 'Failed to unmute channel', - }); - }); - }, - }, ]; return ( diff --git a/public/pages/Emails/components/tables/RecipientGroupsTable.tsx b/public/pages/Emails/components/tables/RecipientGroupsTable.tsx index a29890c5..3b0f96c4 100644 --- a/public/pages/Emails/components/tables/RecipientGroupsTable.tsx +++ b/public/pages/Emails/components/tables/RecipientGroupsTable.tsx @@ -38,7 +38,7 @@ import { isDataSourceError, isDataSourceChanged, } from '../../../../components/MDSEnabledComponent/MDSEnabledComponent'; -import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; +import { NavigationPublicPluginStart, TopNavControlTextData } from 'src/plugins/navigation/public'; interface RecipientGroupsTableProps { coreContext: CoreStart; @@ -221,15 +221,16 @@ export class RecipientGroupsTable extends Component< selectable: () => true, onSelectionChange: this.onSelectionChange, }; + const { HeaderControl } = this.props.navigationUI; const showActionsInHeader = this.props.showActionsInHeader; - const { setAppRightControls } = this.props.application; + const { setAppRightControls, setAppLeftControls } = this.props.application; const headerControls = [ { id: 'Create recipient group', - label: 'Create recipient group', + label: `Create recipient group`, iconType: 'plus', fill: true, href: `#${ROUTES.CREATE_RECIPIENT_GROUP}`, @@ -241,72 +242,57 @@ export class RecipientGroupsTable extends Component< 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: showActionsInHeader ? ( - - ) : ( - - Create recipient group - - ), - } - ]} - /> - } - bodyStyles={{ padding: 'initial' }} - title="Recipient groups" + bodyStyles={!showActionsInHeader ? { padding: 'initial' } : undefined} + title={!showActionsInHeader ? "Recipient groups" : undefined} titleSize="m" - total={this.state.total} + total={!showActionsInHeader ? this.state.total : undefined} > - +
+ + + {/* Always Render Actions but adjust layout based on showActionsInHeader */} +
+ + {({ onShow }) => ( + + onShow(DeleteRecipientGroupModal, { + recipientGroups: this.state.selectedItems, + refresh: this.refresh, + }) + } + > + Delete + + )} + + + location.assign( + `#${ROUTES.EDIT_RECIPIENT_GROUP}/${this.state.selectedItems[0]?.config_id}` + ) + } + > + Edit + + {!showActionsInHeader && ( + + Create recipient group + + )} +
+
- +
+ + {/* Header control should be displayed if showActionsInHeader is true */} + {showActionsInHeader && ( + + )} + {showActionsInHeader && ( + + )} ); } diff --git a/public/pages/Main/Main.tsx b/public/pages/Main/Main.tsx index d546bc01..17472c2c 100644 --- a/public/pages/Main/Main.tsx +++ b/public/pages/Main/Main.tsx @@ -28,7 +28,7 @@ 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'; @@ -37,6 +37,7 @@ import { DataSourceAttributes } from "../../../../../src/plugins/data_source/com import semver from "semver"; import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; import { NavigationMenuContext } from '../../services/NavigationContext'; +import { ComponentType } from 'react'; enum Navigation { Notifications = 'Notifications', @@ -193,7 +194,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(); From 0c1f20e1cbe028c9cc3405be6eba34a7d85f407a Mon Sep 17 00:00:00 2001 From: Riya Saxena Date: Tue, 13 Aug 2024 11:37:00 -0700 Subject: [PATCH 05/21] view channel Signed-off-by: Riya Saxena --- public/pages/Channels/Channels.tsx | 6 +-- .../components/details/ChannelDetails.tsx | 43 +++++++++++++++++-- .../details/ChannelDetailsActions.tsx | 19 +++++--- 3 files changed, 55 insertions(+), 13 deletions(-) diff --git a/public/pages/Channels/Channels.tsx b/public/pages/Channels/Channels.tsx index 663431f7..273eff9b 100644 --- a/public/pages/Channels/Channels.tsx +++ b/public/pages/Channels/Channels.tsx @@ -17,7 +17,7 @@ import { 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 { @@ -43,7 +43,7 @@ import MDSEnabledComponent, { isDataSourceChanged, isDataSourceError, } from '../../components/MDSEnabledComponent/MDSEnabledComponent'; -import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; +import { NavigationPublicPluginStart, TopNavControlButtonData } from 'src/plugins/navigation/public'; import { ApplicationStart } from 'opensearch-dashboards/public'; interface ChannelsProps extends RouteComponentProps, DataSourceMenuProperties { @@ -233,7 +233,7 @@ export class Channels extends MDSEnabledComponent href: `#${ROUTES.CREATE_CHANNEL}`, testId: 'createButton', controlType: 'button', - }, + } as TopNavControlButtonData, ]; return ( diff --git a/public/pages/Channels/components/details/ChannelDetails.tsx b/public/pages/Channels/components/details/ChannelDetails.tsx index a209093e..1fbad83c 100644 --- a/public/pages/Channels/components/details/ChannelDetails.tsx +++ b/public/pages/Channels/components/details/ChannelDetails.tsx @@ -64,7 +64,11 @@ export function ChannelDetails(props: ChannelDetailsProps) {
- {channel && } + {channel && + } {channel && ( @@ -103,6 +107,22 @@ export function ChannelDetails(props: ChannelDetailsProps) { ); }; + const sendTestMessage = async () => { + try { + await servicesContext.notificationService.sendTestMessage( + props.channel.config_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([ @@ -206,6 +226,20 @@ export function ChannelDetails(props: ChannelDetailsProps) { ), }, + { + renderComponent: ( +
+ + Send test message + +
+ ), + }, ]} bodyStyles={{ padding: 'initial' }} title="Channel Details" @@ -227,7 +261,10 @@ export function ChannelDetails(props: ChannelDetailsProps) {
- {channel && } + {channel && ( @@ -286,8 +323,8 @@ export function ChannelDetails(props: ChannelDetailsProps) { >
- ); + }; diff --git a/public/pages/Channels/components/details/ChannelDetailsActions.tsx b/public/pages/Channels/components/details/ChannelDetailsActions.tsx index e7bb7d1e..c5bb359d 100644 --- a/public/pages/Channels/components/details/ChannelDetailsActions.tsx +++ b/public/pages/Channels/components/details/ChannelDetailsActions.tsx @@ -30,6 +30,7 @@ interface ChannelDetailsActionsParams { interface ChannelDetailsActionsProps { channel: ChannelItemType; + showActionsInHeader: Boolean; } export function ChannelDetailsActions(props: ChannelDetailsActionsProps) { @@ -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 showActionsInHeader is false + if (!props.showActionsInHeader) { + actions.splice(1, 0, { + label: 'Send test message', + disabled: + !props.channel.config_id || + !props.channel.is_enabled, + action: sendTestMessage, + }); + } + return ( {({ onShow }) => ( From 04591078e9f168b0d5be1bef1cb7f536542691a2 Mon Sep 17 00:00:00 2001 From: Riya Saxena Date: Tue, 13 Aug 2024 12:57:46 -0700 Subject: [PATCH 06/21] view channel Signed-off-by: Riya Saxena --- public/pages/Channels/Channels.tsx | 75 +++++++++++++----------------- 1 file changed, 33 insertions(+), 42 deletions(-) diff --git a/public/pages/Channels/Channels.tsx b/public/pages/Channels/Channels.tsx index 273eff9b..8e984ef5 100644 --- a/public/pages/Channels/Channels.tsx +++ b/public/pages/Channels/Channels.tsx @@ -256,9 +256,19 @@ export class Channels extends MDSEnabledComponent }, { component: !showActionsInHeader && ( - - Create channel - +
+ {/* Channel Actions next to Create Channel button */} + this.setState({ selectedItems })} + items={this.state.items} + setItems={(items) => this.setState({ items })} + refresh={this.refresh} + /> + + Create channel + +
), }, ]} @@ -266,45 +276,26 @@ export class Channels extends MDSEnabledComponent } >
- {showActionsInHeader ? ( -
- {/* Channel Controls with Status and Type dropdowns */} - - {/* Channel Actions next to Status and Type dropdowns */} - this.setState({ selectedItems })} - items={this.state.items} - setItems={(items) => this.setState({ items })} - refresh={this.refresh} - /> -
- ) : ( -
- {/* Channel Controls with Status and Type dropdowns */} - - - {/* Channel Actions next to Create Channel button */} - this.setState({ selectedItems })} - items={this.state.items} - setItems={(items) => this.setState({ items })} - refresh={this.refresh} - /> - - Create channel - -
- )} +
+ {/* Channel Controls with Status and Type dropdowns */} + + {showActionsInHeader && ( +
+ {/* Channel Actions next to Status and Type dropdowns */} + this.setState({ selectedItems })} + items={this.state.items} + setItems={(items) => this.setState({ items })} + refresh={this.refresh} + /> +
+ )} +
Date: Tue, 13 Aug 2024 18:12:48 -0700 Subject: [PATCH 07/21] channel details fixed Signed-off-by: Riya Saxena --- .../components/details/ChannelDetails.tsx | 207 +++++++++--------- 1 file changed, 103 insertions(+), 104 deletions(-) diff --git a/public/pages/Channels/components/details/ChannelDetails.tsx b/public/pages/Channels/components/details/ChannelDetails.tsx index 1fbad83c..54f9d4e1 100644 --- a/public/pages/Channels/components/details/ChannelDetails.tsx +++ b/public/pages/Channels/components/details/ChannelDetails.tsx @@ -42,76 +42,75 @@ interface ChannelDetailsProps extends RouteComponentProps<{ application: ApplicationStart; } -export function ChannelDetails(props: ChannelDetailsProps) { +const ChannelHeaderContent = ({ channel, setChannel, showActionsInHeader }) => { const coreContext = useContext(CoreServicesContext)!; const servicesContext = useContext(ServicesContext)!; - const id = props.match.params.id; - const [channel, setChannel] = useState(); - const [toasts, setToasts] = useState([]); - - const ChannelHeaderContent = () => { - const coreContext = useContext(CoreServicesContext)!; - const servicesContext = useContext(ServicesContext)!; - return ( - - - {channel?.is_enabled === undefined ? null : channel.is_enabled ? ( - Active - ) : ( - Muted - )} - - - - {channel && + return ( + + + {channel?.is_enabled === undefined ? null : channel.is_enabled ? ( + Active + ) : ( + Muted + )} + + + + {channel && ( } - - - {channel && ( - - {({ onShow }) => ( - { - if (!channel) return; - if (channel.is_enabled) { - onShow(MuteChannelModal, { - selected: [channel], - setSelected: (selected) => setChannel(selected[0]), + /> + )} + + + {channel && ( + + {({ onShow }) => ( + { + if (channel.is_enabled) { + onShow(MuteChannelModal, { + selected: [channel], + setSelected: (selected) => 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); }); - } 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'} - - )} - - )} - - - ); - }; + } + }} + > + {channel.is_enabled ? 'Mute channel' : 'Unmute channel'} + + )} +
+ )} + + + ); +}; + + +export function ChannelDetails(props: ChannelDetailsProps) { + const coreContext = useContext(CoreServicesContext)!; + const servicesContext = useContext(ServicesContext)!; + const id = props.match.params.id; + const [channel, setChannel] = useState(); + const [toasts, setToasts] = useState([]); const sendTestMessage = async () => { try { - await servicesContext.notificationService.sendTestMessage( - props.channel.config_id, - ); + await servicesContext.notificationService.sendTestMessage(id); coreContext.notifications.toasts.addSuccess( 'Successfully sent a test message.' ); @@ -121,8 +120,7 @@ export function ChannelDetails(props: ChannelDetailsProps) { toastMessage: 'View error details and adjust the channel settings.', }); } - } - + }; useEffect(() => { coreContext.chrome.setBreadcrumbs([ @@ -208,44 +206,44 @@ export function ChannelDetails(props: ChannelDetailsProps) { }, ]; - const { HeaderControl } = props.navigationUI; const showActionsInHeader = props.showActionsInHeader; const { setAppRightControls } = props.application; - return ( <> {showActionsInHeader ? ( - <> - - ), - }, - { - renderComponent: ( -
- - Send test message - -
- ), - }, - ]} - bodyStyles={{ padding: 'initial' }} - title="Channel Details" - titleSize="m" - /> - + + ), + }, + { + renderComponent: ( +
+ + Send test message + +
+ ), + }, + ]} + bodyStyles={{ padding: 'initial' }} + title="Channel Details" + titleSize="m" + /> ) : (
@@ -261,10 +259,12 @@ export function ChannelDetails(props: ChannelDetailsProps) { - + {channel && ( + + )} {channel && ( @@ -272,9 +272,8 @@ export function ChannelDetails(props: ChannelDetailsProps) { {({ onShow }) => ( { - if (!channel) return; if (channel.is_enabled) { onShow(MuteChannelModal, { selected: [channel], @@ -293,7 +292,7 @@ export function ChannelDetails(props: ChannelDetailsProps) { } }} > - {channel?.is_enabled ? 'Mute channel' : 'Unmute channel'} + {channel.is_enabled ? 'Mute channel' : 'Unmute channel'} )} @@ -302,7 +301,7 @@ export function ChannelDetails(props: ChannelDetailsProps) {
)} - + - + - + ); - + }; From 229f18eb542a0c61127ab95623a3f402515590cb Mon Sep 17 00:00:00 2001 From: Riya Saxena Date: Wed, 14 Aug 2024 14:04:46 -0700 Subject: [PATCH 08/21] fixed many things Signed-off-by: Riya Saxena --- models/interfaces.ts | 1 + public/pages/Channels/Channels.tsx | 223 ++++++++----- public/pages/Emails/EmailSenders.tsx | 6 + .../tables/RecipientGroupsTable.tsx | 312 +++++++++++++----- .../components/tables/SESSendersTable.tsx | 306 ++++++++++++----- .../Emails/components/tables/SendersTable.tsx | 291 +++++++++++----- 6 files changed, 811 insertions(+), 328 deletions(-) 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/public/pages/Channels/Channels.tsx b/public/pages/Channels/Channels.tsx index 8e984ef5..ca7804b3 100644 --- a/public/pages/Channels/Channels.tsx +++ b/public/pages/Channels/Channels.tsx @@ -7,11 +7,14 @@ import { EuiBasicTable, EuiButton, EuiEmptyPrompt, + EuiFlexGroup, + EuiFlexItem, EuiHealth, EuiHorizontalRule, EuiLink, EuiTableFieldDataColumnType, EuiTableSortingType, + EuiTitle, SortDirection, } from '@elastic/eui'; import { Criteria } from '@elastic/eui/src/components/basic_table/basic_table'; @@ -222,7 +225,7 @@ export class Channels extends MDSEnabledComponent const { HeaderControl } = this.props.navigationUI; const showActionsInHeader = this.props.showActionsInHeader; - const { setAppRightControls } = this.props.application; + const { setAppRightControls, setAppLeftControls } = this.props.application; const headerControls = [ { @@ -235,96 +238,162 @@ export class Channels extends MDSEnabledComponent controlType: 'button', } as TopNavControlButtonData, ]; - + + const totalChannnels = ( + +

({this.state.total})

+
+ ) + return ( <> - - ) : null, - }, - { - component: !showActionsInHeader && ( -
- {/* Channel Actions next to Create Channel button */} - this.setState({ selectedItems })} - items={this.state.items} - setItems={(items) => this.setState({ items })} - refresh={this.refresh} + {showActionsInHeader ? ( + - - Create channel - -
- ), - }, - ]} - /> - } - > -
-
- {/* Channel Controls with Status and Type dropdowns */} - + ) + } + ]} /> - {showActionsInHeader && ( + } + > + +
+
+
- {/* Channel Actions next to Status and Type dropdowns */} this.setState({ selectedItems })} + setSelected={(selectedItems: ChannelItemType[]) => + this.setState({ selectedItems }) + } items={this.state.items} - setItems={(items) => this.setState({ items })} + setItems={(items: ChannelItemType[]) => + this.setState({ items }) + } refresh={this.refresh} />
- )} +
-
- - No channels to display} - body="To send or receive notifications, you will need to create a notification channel." - actions={ - - Create channel - - } + + 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} + /> + + + + ) : ( + + this.setState({ selectedItems }) + } + items={this.state.items} + setItems={(items: ChannelItemType[]) => + this.setState({ items }) + } + refresh={this.refresh} + /> + ), + }, + { + component: ( + + Create channel + + ), + }, + ]} /> } - onChange={this.onTableChange} - pagination={pagination} - sorting={sorting} - tableLayout="auto" - loading={this.state.loading} - /> - + bodyStyles={{ padding: 'initial' }} + title="Channels" + titleSize="m" + total={this.state.total} + > + + + + 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} + /> + + + )} ); - - } -} +}; + diff --git a/public/pages/Emails/EmailSenders.tsx b/public/pages/Emails/EmailSenders.tsx index c83768cc..0fbcd536 100644 --- a/public/pages/Emails/EmailSenders.tsx +++ b/public/pages/Emails/EmailSenders.tsx @@ -49,6 +49,9 @@ export function EmailSenders(props: EmailSendersProps) { )} @@ -59,6 +62,9 @@ export function EmailSenders(props: EmailSendersProps) { )} diff --git a/public/pages/Emails/components/tables/RecipientGroupsTable.tsx b/public/pages/Emails/components/tables/RecipientGroupsTable.tsx index 3b0f96c4..0f6b88c5 100644 --- a/public/pages/Emails/components/tables/RecipientGroupsTable.tsx +++ b/public/pages/Emails/components/tables/RecipientGroupsTable.tsx @@ -6,12 +6,17 @@ import { EuiBasicTable, EuiButton, + EuiContextMenuItem, EuiEmptyPrompt, EuiFieldSearch, + EuiFlexGroup, + EuiFlexItem, EuiHorizontalRule, EuiLink, + EuiPopover, EuiTableFieldDataColumnType, EuiTableSortingType, + EuiTitle, SortDirection, } from '@elastic/eui'; import { Criteria } from '@elastic/eui/src/components/basic_table/basic_table'; @@ -38,7 +43,7 @@ import { isDataSourceError, isDataSourceChanged, } from '../../../../components/MDSEnabledComponent/MDSEnabledComponent'; -import { NavigationPublicPluginStart, TopNavControlTextData } from 'src/plugins/navigation/public'; +import { NavigationPublicPluginStart, TopNavControlButtonData, TopNavControlTextData } from 'src/plugins/navigation/public'; interface RecipientGroupsTableProps { coreContext: CoreStart; @@ -49,7 +54,7 @@ interface RecipientGroupsTableProps { } interface RecipientGroupsTableState - extends TableState {} + extends TableState { } export class RecipientGroupsTable extends Component< RecipientGroupsTableProps, @@ -200,6 +205,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); @@ -221,117 +232,240 @@ export class RecipientGroupsTable extends Component< selectable: () => true, onSelectionChange: this.onSelectionChange, }; - const { HeaderControl } = this.props.navigationUI; const showActionsInHeader = this.props.showActionsInHeader; const { setAppRightControls, setAppLeftControls } = this.props.application; + 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`, + label: 'Create recipient group', iconType: 'plus', fill: true, href: `#${ROUTES.CREATE_RECIPIENT_GROUP}`, testId: 'createButton', controlType: 'button', - }, + } as TopNavControlButtonData, ]; + const totalEmailGroups = ( + +

({this.state.total})

+
+ ) + return ( <> - -
+ {showActionsInHeader ? ( + + + ), + }, + { + component: ( + + ) + } + ]} + /> + } + > + + + + + + + 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} + + )} + + ))} + + + + + + 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} + /> + + + ) : ( + + {({ 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 + + ), + }, + ]} + /> + } + bodyStyles={{ padding: 'initial' }} + title="Recipient groups" + titleSize="m" + total={this.state.total} + > - - {/* Always Render Actions but adjust layout based on showActionsInHeader */} -
- - {({ onShow }) => ( - - onShow(DeleteRecipientGroupModal, { - recipientGroups: this.state.selectedItems, - refresh: this.refresh, - }) - } - > - Delete - - )} - - - location.assign( - `#${ROUTES.EDIT_RECIPIENT_GROUP}/${this.state.selectedItems[0]?.config_id}` - ) - } - > - Edit - - {!showActionsInHeader && ( - - Create recipient group - - )} -
-
- - - 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} - /> -
- - {/* Header control should be displayed if showActionsInHeader is true */} - {showActionsInHeader && ( - - )} - {showActionsInHeader && ( - + + + 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} + /> + )} ); } -} +}; diff --git a/public/pages/Emails/components/tables/SESSendersTable.tsx b/public/pages/Emails/components/tables/SESSendersTable.tsx index bf749009..fbdb568c 100644 --- a/public/pages/Emails/components/tables/SESSendersTable.tsx +++ b/public/pages/Emails/components/tables/SESSendersTable.tsx @@ -6,9 +6,14 @@ import { EuiBasicTable, EuiButton, + EuiContextMenuItem, EuiEmptyPrompt, EuiFieldSearch, + EuiFlexGroup, + EuiFlexItem, EuiHorizontalRule, + EuiPopover, + EuiSelect, EuiTableFieldDataColumnType, EuiTableSortingType, SortDirection, @@ -16,15 +21,14 @@ import { 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 } from 'react'; -import { CoreStart } from '../../../../../../../src/core/public'; +import React, { Component, useContext, useState } from 'react'; +import { ApplicationStart, CoreStart } from '../../../../../../../src/core/public'; import { SESSenderItemType, TableState, } from '../../../../../models/interfaces'; import { - ContentPanel, - ContentPanelActions, + ContentPanel, ContentPanelActions, } from '../../../../components/ContentPanel'; import { ModalConsumer } from '../../../../components/Modal'; import { NotificationService, ServicesContext } from '../../../../services'; @@ -36,13 +40,17 @@ import { isDataSourceError, isDataSourceChanged, } from '../../../../components/MDSEnabledComponent/MDSEnabledComponent'; +import { NavigationPublicPluginStart, TopNavControlButtonData } from 'src/plugins/navigation/public'; interface SESSendersTableProps { coreContext: CoreStart; notificationService: NotificationService; + navigationUI: NavigationPublicPluginStart['ui']; + showActionsInHeader: boolean; + application: ApplicationStart; } -interface SESSendersTableState extends TableState {} +interface SESSendersTableState extends TableState { } export class SESSendersTable extends Component< SESSendersTableProps, @@ -63,6 +71,7 @@ export class SESSendersTable extends Component< items: [], selectedItems: [], loading: true, + isPopoverOpen: false, // Initialize popover state }; this.columns = [ @@ -160,6 +169,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 +197,216 @@ export class SESSendersTable extends Component< onSelectionChange: this.onSelectionChange, }; + const showActionsInHeader = this.props.showActionsInHeader; + + 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, + }, + }, + ]; + return ( - - {({ onShow }) => ( + <> + {showActionsInHeader ? ( + + Create SES sender + + ), + }, + ]} + /> + } + bodyStyles={{ padding: 'initial' }} + title="SES senders" + titleSize="m" + total={this.state.total} + > + + + + + + + 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} + + )} + + ))} + + + + + + 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" + /> + + ) : ( + + {({ 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: ( + + Create SES sender - )} - - ), - }, - { - 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} - > - - + ), + }, + ]} + /> + } + bodyStyles={{ padding: 'initial' }} + title="SES senders" + titleSize="m" + total={this.state.total} + > + + - 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 - + 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" /> - } - 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 ebbf5d9d..64c2ab3a 100644 --- a/public/pages/Emails/components/tables/SendersTable.tsx +++ b/public/pages/Emails/components/tables/SendersTable.tsx @@ -6,8 +6,13 @@ import { EuiBasicTable, EuiButton, + EuiContextMenuItem, EuiEmptyPrompt, + EuiFieldSearch, + EuiFlexGroup, + EuiFlexItem, EuiHorizontalRule, + EuiPopover, EuiTableFieldDataColumnType, EuiTableSortingType, SortDirection, @@ -16,7 +21,7 @@ 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 } from 'react'; -import { CoreStart } from '../../../../../../../src/core/public'; +import { ApplicationStart, CoreStart } from '../../../../../../../src/core/public'; import { SenderItemType, TableState } from '../../../../../models/interfaces'; import { ContentPanel, @@ -36,10 +41,14 @@ import { isDataSourceError, isDataSourceChanged, } from '../../../../components/MDSEnabledComponent/MDSEnabledComponent'; +import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; interface SendersTableProps { coreContext: CoreStart; notificationService: NotificationService; + navigationUI: NavigationPublicPluginStart['ui']; + showActionsInHeader: boolean; + application: ApplicationStart; } interface SendersTableState extends TableState { @@ -69,6 +78,7 @@ export class SendersTable extends Component< filters: { encryptionMethod: [], }, + isPopoverOpen: false, // Initialize popover state }; this.columns = [ @@ -181,6 +191,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 +219,210 @@ export class SendersTable extends Component< onSelectionChange: this.onSelectionChange, }; + const showActionsInHeader = this.props.showActionsInHeader; + + 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, + }, + }, + ]; + return ( - - {({ onShow }) => ( + <> + {showActionsInHeader ? ( + + Create SMTP sender + + ), + }, + ]} + /> + } + bodyStyles={{ padding: 'initial' }} + title="SMTP senders" + titleSize="m" + total={this.state.total} + > + + + this.setState({ filters })} + /> + + + + 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} + + )} + + ))} + + + + + + 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} + /> + + ) : ( + + {({ 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: ( + + Create SMTP sender - )} - - ), - }, - { - 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 })} - /> - + ), + }, + ]} + /> + } + 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 - + 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} /> - } - onChange={this.onTableChange} - pagination={pagination} - sorting={sorting} - loading={this.state.loading} - /> - + + )} + ); } -} +}; From 9ebcf498468d2d425bc97c2bb34dee68f3e0f63f Mon Sep 17 00:00:00 2001 From: Riya Saxena Date: Wed, 14 Aug 2024 14:27:15 -0700 Subject: [PATCH 09/21] bug fixes Signed-off-by: Riya Saxena --- .../components/details/ChannelDetails.tsx | 105 ++++++++++-------- 1 file changed, 60 insertions(+), 45 deletions(-) diff --git a/public/pages/Channels/components/details/ChannelDetails.tsx b/public/pages/Channels/components/details/ChannelDetails.tsx index 54f9d4e1..b5824e49 100644 --- a/public/pages/Channels/components/details/ChannelDetails.tsx +++ b/public/pages/Channels/components/details/ChannelDetails.tsx @@ -48,13 +48,6 @@ const ChannelHeaderContent = ({ channel, setChannel, showActionsInHeader }) => { return ( - - {channel?.is_enabled === undefined ? null : channel.is_enabled ? ( - Active - ) : ( - Muted - )} - {channel && ( @@ -75,7 +68,7 @@ const ChannelHeaderContent = ({ channel, setChannel, showActionsInHeader }) => { if (channel.is_enabled) { onShow(MuteChannelModal, { selected: [channel], - setSelected: (selected) => setChannel(selected[0]), + setSelected: (selected: any[]) => setChannel(selected[0]), }); } else { const newChannel = { ...channel, is_enabled: true }; @@ -208,42 +201,64 @@ export function ChannelDetails(props: ChannelDetailsProps) { const { HeaderControl } = props.navigationUI; const showActionsInHeader = props.showActionsInHeader; - const { setAppRightControls } = props.application; + const { setAppRightControls, setAppLeftControls } = props.application; return ( <> {showActionsInHeader ? ( - - ), - }, - { - renderComponent: ( -
- - Send test message - -
- ), - }, - ]} - bodyStyles={{ padding: 'initial' }} - title="Channel Details" - titleSize="m" - /> + <> + + ), + }, + { + renderComponent: ( +
+ + Send test message + +
+ ), + }, + ]} + bodyStyles={{ padding: 'initial' }} + title="Channel Details" + titleSize="m" + /> + + + {channel?.is_enabled === undefined ? null : channel.is_enabled ? ( + Active + ) : ( + Muted + )} +
+ ), + }, + ]} + bodyStyles={{ padding: 'initial' }} + title="Channel Details" + titleSize="m" + /> + ) : (
@@ -277,7 +292,7 @@ export function ChannelDetails(props: ChannelDetailsProps) { if (channel.is_enabled) { onShow(MuteChannelModal, { selected: [channel], - setSelected: (selected) => setChannel(selected[0]), + setSelected: (selected: React.SetStateAction[]) => setChannel(selected[0]), }); } else { const newChannel = { ...channel, is_enabled: true }; @@ -301,7 +316,7 @@ export function ChannelDetails(props: ChannelDetailsProps) {
)} - + - + - + ); - + }; From d48f44152546c64a1b67be2e50ee4667b633e00f Mon Sep 17 00:00:00 2001 From: Riya Saxena Date: Wed, 14 Aug 2024 15:56:52 -0700 Subject: [PATCH 10/21] fix tests page header changes Signed-off-by: Riya Saxena --- public/pages/Channels/Channels.tsx | 6 +- .../ChannelDetails.test.tsx.snap | 137 ++++++++---------- .../components/details/ChannelDetails.tsx | 6 +- public/pages/CreateChannel/CreateChannel.tsx | 6 +- .../tables/RecipientGroupsTable.tsx | 6 +- public/pages/Main/Main.tsx | 26 ++-- 6 files changed, 91 insertions(+), 96 deletions(-) diff --git a/public/pages/Channels/Channels.tsx b/public/pages/Channels/Channels.tsx index ca7804b3..3aa893a9 100644 --- a/public/pages/Channels/Channels.tsx +++ b/public/pages/Channels/Channels.tsx @@ -223,9 +223,11 @@ export class Channels extends MDSEnabledComponent onSelectionChange: this.onSelectionChange, }; - const { HeaderControl } = this.props.navigationUI; + const navigationUI = this.props?.navigationUI || {}; + const { HeaderControl } = navigationUI; const showActionsInHeader = this.props.showActionsInHeader; - const { setAppRightControls, setAppLeftControls } = this.props.application; + const appllication = this.props?.application || {}; + const { setAppRightControls, setAppLeftControls } = appllication; const headerControls = [ { diff --git a/public/pages/Channels/__tests__/__snapshots__/ChannelDetails.test.tsx.snap b/public/pages/Channels/__tests__/__snapshots__/ChannelDetails.test.tsx.snap index 2cdaf728..daf045d7 100644 --- a/public/pages/Channels/__tests__/__snapshots__/ChannelDetails.test.tsx.snap +++ b/public/pages/Channels/__tests__/__snapshots__/ChannelDetails.test.tsx.snap @@ -2,45 +2,29 @@ exports[` spec handles a non-existing channel 1`] = `
-
-
+
+

+ - +

-
-

- - -

-
-
-
+ class="euiFlexItem euiFlexItem--flexGrowZero" + style="padding-bottom: 5px;" + /> +
+
+
-
-
-
spec handles a non-existing channel 1`] = ` exports[` spec renders a specific channel 1`] = `
-
-
+
+

+ - +

-
-

- - -

-
-
-
+ class="euiFlexItem euiFlexItem--flexGrowZero" + style="padding-bottom: 5px;" + /> +
+
+
-
-
-
spec renders a specific channel 1`] = ` `; exports[` spec renders the component 1`] = ` -
+
+

+ - +

+
+
+
+
+
+
+
`; diff --git a/public/pages/Channels/components/details/ChannelDetails.tsx b/public/pages/Channels/components/details/ChannelDetails.tsx index b5824e49..f4764bb1 100644 --- a/public/pages/Channels/components/details/ChannelDetails.tsx +++ b/public/pages/Channels/components/details/ChannelDetails.tsx @@ -199,9 +199,11 @@ export function ChannelDetails(props: ChannelDetailsProps) { }, ]; - const { HeaderControl } = props.navigationUI; + const navigationUI = props?.navigationUI || {}; + const { HeaderControl } = navigationUI; const showActionsInHeader = props.showActionsInHeader; - const { setAppRightControls, setAppLeftControls } = props.application; + const appllication = props?.application || {}; + const { setAppRightControls, setAppLeftControls } = appllication; return ( <> diff --git a/public/pages/CreateChannel/CreateChannel.tsx b/public/pages/CreateChannel/CreateChannel.tsx index e9cceae9..fe589f2c 100644 --- a/public/pages/CreateChannel/CreateChannel.tsx +++ b/public/pages/CreateChannel/CreateChannel.tsx @@ -147,9 +147,11 @@ export function CreateChannel(props: CreateChannelsProps) { roleArn: [], }); - const { HeaderControl } = props.navigationUI; + const navigationUI = props?.navigationUI || {}; + const { HeaderControl } = navigationUI; const showActionsInHeader = props.showActionsInHeader; - const { setAppDescriptionControls } = props.application; + const appllication = props?.application || {}; + const { setAppDescriptionControls } = appllication; useEffect(() => { coreContext.chrome.setBreadcrumbs([ diff --git a/public/pages/Emails/components/tables/RecipientGroupsTable.tsx b/public/pages/Emails/components/tables/RecipientGroupsTable.tsx index 0f6b88c5..1da1a7e9 100644 --- a/public/pages/Emails/components/tables/RecipientGroupsTable.tsx +++ b/public/pages/Emails/components/tables/RecipientGroupsTable.tsx @@ -233,9 +233,11 @@ export class RecipientGroupsTable extends Component< onSelectionChange: this.onSelectionChange, }; - const { HeaderControl } = this.props.navigationUI; + const navigationUI = this.props?.navigationUI || {}; + const { HeaderControl } = navigationUI; const showActionsInHeader = this.props.showActionsInHeader; - const { setAppRightControls, setAppLeftControls } = this.props.application; + const appllication = this.props?.application || {}; + const { setAppRightControls, setAppLeftControls } = appllication; const actions = [ { diff --git a/public/pages/Main/Main.tsx b/public/pages/Main/Main.tsx index 17472c2c..399502d1 100644 --- a/public/pages/Main/Main.tsx +++ b/public/pages/Main/Main.tsx @@ -249,7 +249,7 @@ export default class Main extends Component { { render={(routeProps: RouteComponentProps) => ( @@ -385,7 +385,7 @@ export default class Main extends Component { @@ -396,7 +396,7 @@ export default class Main extends Component { render={(routeProps: RouteComponentProps<{ id: string }>) => ( @@ -408,7 +408,7 @@ export default class Main extends Component { @@ -420,7 +420,7 @@ export default class Main extends Component { @@ -432,7 +432,7 @@ export default class Main extends Component { @@ -443,7 +443,7 @@ export default class Main extends Component { render={(routeProps: RouteComponentProps) => ( @@ -455,7 +455,7 @@ export default class Main extends Component { @@ -466,7 +466,7 @@ export default class Main extends Component { render={(routeProps: RouteComponentProps) => ( @@ -478,7 +478,7 @@ export default class Main extends Component { @@ -489,7 +489,7 @@ export default class Main extends Component { render={(routeProps: RouteComponentProps) => ( @@ -501,7 +501,7 @@ export default class Main extends Component { From e7e6be4c16ca8ef8ac4b1c319445362a4d77b62f Mon Sep 17 00:00:00 2001 From: Riya Saxena Date: Thu, 15 Aug 2024 11:14:00 -0700 Subject: [PATCH 11/21] code refactored Signed-off-by: Riya Saxena --- opensearch_dashboards.json | 3 +- public/application.tsx | 1 - public/components/PageHeader/PageHeader.tsx | 43 ++++ public/pages/Channels/Channels.tsx | 42 +--- .../components/ChannelActionsHeader.tsx | 89 ------- .../components/details/ChannelDetails.tsx | 229 ++++++++---------- .../details/ChannelDetailsActions.tsx | 6 +- public/pages/CreateChannel/CreateChannel.tsx | 44 ++-- public/pages/Emails/CreateRecipientGroup.tsx | 12 +- public/pages/Emails/CreateSESSender.tsx | 13 +- public/pages/Emails/CreateSender.tsx | 13 +- public/pages/Emails/EmailGroups.tsx | 11 +- public/pages/Emails/EmailSenders.tsx | 20 +- .../__tests__/CreateSenderForm.test.tsx | 5 + .../Emails/__tests__/EmailSenders.test.tsx | 7 +- .../__tests__/SendersTableControls.test.tsx | 5 + .../tables/RecipientGroupsTable.tsx | 33 +-- .../components/tables/SESSendersTable.tsx | 14 +- .../Emails/components/tables/SendersTable.tsx | 11 +- public/pages/Main/Main.tsx | 51 +--- public/plugin.ts | 11 +- public/services/NavigationContext.ts | 19 -- public/services/utils/constants.ts | 28 +++ public/utils/constants.ts | 9 +- test/utils/helpers.ts | 21 ++ 25 files changed, 295 insertions(+), 445 deletions(-) create mode 100644 public/components/PageHeader/PageHeader.tsx delete mode 100644 public/pages/Channels/components/ChannelActionsHeader.tsx delete mode 100644 public/services/NavigationContext.ts create mode 100644 public/services/utils/constants.ts create mode 100644 test/utils/helpers.ts diff --git a/opensearch_dashboards.json b/opensearch_dashboards.json index 743b1488..86d16bee 100644 --- a/opensearch_dashboards.json +++ b/opensearch_dashboards.json @@ -4,7 +4,8 @@ "opensearchDashboardsVersion": "3.0.0", "requiredPlugins": [ "navigation", - "data" + "data", + "opensearchDashboardsUtils" ], "optionalPlugins": [ "share", diff --git a/public/application.tsx b/public/application.tsx index b4bef5c5..11b72d82 100644 --- a/public/application.tsx +++ b/public/application.tsx @@ -31,7 +31,6 @@ export const renderApp = ( dataSourceManagement={dataSourceManagement} http={coreStart.http} // Pass http as a prop defaultRoute={defaultRoute} - navigation={pluginStartDependencies.navigation} /> )} 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 3aa893a9..53a157ed 100644 --- a/public/pages/Channels/Channels.tsx +++ b/public/pages/Channels/Channels.tsx @@ -7,8 +7,6 @@ import { EuiBasicTable, EuiButton, EuiEmptyPrompt, - EuiFlexGroup, - EuiFlexItem, EuiHealth, EuiHorizontalRule, EuiLink, @@ -32,6 +30,7 @@ import { NotificationService } from '../../services'; import { BREADCRUMBS, ROUTES, + setBreadcrumbs, } from '../../utils/constants'; import { CHANNEL_TYPE, @@ -46,14 +45,12 @@ import MDSEnabledComponent, { isDataSourceChanged, isDataSourceError, } from '../../components/MDSEnabledComponent/MDSEnabledComponent'; -import { NavigationPublicPluginStart, TopNavControlButtonData } from 'src/plugins/navigation/public'; -import { ApplicationStart } from 'opensearch-dashboards/public'; +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; - navigationUI: NavigationPublicPluginStart['ui']; - showActionsInHeader: boolean; - application: ApplicationStart; } interface ChannelsState extends TableState, DataSourceMenuProperties { @@ -123,7 +120,7 @@ export class Channels extends MDSEnabledComponent } async componentDidMount() { - this.context.chrome.setBreadcrumbs([ + setBreadcrumbs([ BREADCRUMBS.NOTIFICATIONS, BREADCRUMBS.CHANNELS, ]); @@ -223,12 +220,6 @@ export class Channels extends MDSEnabledComponent onSelectionChange: this.onSelectionChange, }; - const navigationUI = this.props?.navigationUI || {}; - const { HeaderControl } = navigationUI; - const showActionsInHeader = this.props.showActionsInHeader; - const appllication = this.props?.application || {}; - const { setAppRightControls, setAppLeftControls } = appllication; - const headerControls = [ { id: 'Create Channel', @@ -241,7 +232,7 @@ export class Channels extends MDSEnabledComponent } as TopNavControlButtonData, ]; - const totalChannnels = ( + const totalChannels = (

({this.state.total})

@@ -249,34 +240,25 @@ export class Channels extends MDSEnabledComponent return ( <> - {showActionsInHeader ? ( + {getUseUpdatedUx() ? ( - ), - }, - { - component: ( - - ) + ), } ]} /> } > -
void; -} - -interface ChannelActionsProps { - selected: ChannelItemType[]; - setSelected: (items: ChannelItemType[]) => void; - items: ChannelItemType[]; - setItems: (items: ChannelItemType[]) => void; - refresh: () => void; - href?: string; -} - -export function ChannelActionsHeader(props: ChannelActionsProps) { - const [isPopoverOpen, setIsPopoverOpen] = useState(false); - - const actions: ChannelActionsParams[] = [ - { - label: 'Edit', - disabled: props.selected.length !== 1, - href: props.href, - }, - { - label: 'Delete', - disabled: props.selected.length === 0, - modal: DeleteChannelModal, - modalParams: { refresh: props.refresh }, - }, - ]; - - return ( - - {({ onShow }) => ( - setIsPopoverOpen(!isPopoverOpen)} - > - Actions - - } - isOpen={isPopoverOpen} - closePopover={() => setIsPopoverOpen(false)} - > - {actions.map((params) => ( - { - setIsPopoverOpen(false); - if (params.modal) { - onShow(params.modal, { - selected: props.selected, - ...(params.modalParams || {}), - }); - } - if (params.href) location.assign(params.href); - if (params.action) params.action(); - }} - > - {params.label} - - ))} - - )} - - ); -} diff --git a/public/pages/Channels/components/details/ChannelDetails.tsx b/public/pages/Channels/components/details/ChannelDetails.tsx index f4764bb1..d1ce6361 100644 --- a/public/pages/Channels/components/details/ChannelDetails.tsx +++ b/public/pages/Channels/components/details/ChannelDetails.tsx @@ -7,7 +7,6 @@ import { EuiButton, EuiFlexGroup, EuiFlexItem, - EuiGlobalToastList, EuiHealth, EuiSpacer, EuiText, @@ -24,6 +23,7 @@ import { ServicesContext } from '../../../../services'; import { BREADCRUMBS, ROUTES, + setBreadcrumbs, } from '../../../../utils/constants'; import { renderTime } from '../../../../utils/helpers'; import { ListItemType } from '../../types'; @@ -31,18 +31,14 @@ import { MuteChannelModal } from '../modals/MuteChannelModal'; import { ChannelDetailItems } from './ChannelDetailItems'; import { ChannelDetailsActions } from './ChannelDetailsActions'; import { ChannelSettingsDetails } from './ChannelSettingsDetails'; -import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; -import { ApplicationStart } from 'opensearch-dashboards/public'; +import { getUseUpdatedUx } from '../../../../services/utils/constants'; +import PageHeader from "../../../../components/PageHeader/PageHeader"; interface ChannelDetailsProps extends RouteComponentProps<{ id: string -}> { - navigationUI: NavigationPublicPluginStart['ui']; - showActionsInHeader: boolean; - application: ApplicationStart; -} +}> {} -const ChannelHeaderContent = ({ channel, setChannel, showActionsInHeader }) => { +const ChannelHeaderContent = ({ channel, setChannel }) => { const coreContext = useContext(CoreServicesContext)!; const servicesContext = useContext(ServicesContext)!; @@ -53,7 +49,6 @@ const ChannelHeaderContent = ({ channel, setChannel, showActionsInHeader }) => { {channel && ( )} @@ -116,7 +111,7 @@ export function ChannelDetails(props: ChannelDetailsProps) { }; useEffect(() => { - coreContext.chrome.setBreadcrumbs([ + setBreadcrumbs([ BREADCRUMBS.NOTIFICATIONS, BREADCRUMBS.CHANNELS, ]); @@ -142,7 +137,7 @@ export function ChannelDetails(props: ChannelDetailsProps) { }) .then((response) => { setChannel(response); - coreContext.chrome.setBreadcrumbs([ + setBreadcrumbs([ BREADCRUMBS.NOTIFICATIONS, BREADCRUMBS.CHANNELS, { @@ -199,125 +194,108 @@ export function ChannelDetails(props: ChannelDetailsProps) { }, ]; - const navigationUI = props?.navigationUI || {}; - const { HeaderControl } = navigationUI; - const showActionsInHeader = props.showActionsInHeader; - const appllication = props?.application || {}; - const { setAppRightControls, setAppLeftControls } = appllication; + const rightControls = [ + { + renderComponent: ( + + ), + }, + { + renderComponent: ( +
+ + Send test message + +
+ ), + }, + ]; + + const badgeControls = [ + { + renderComponent: ( + + {channel?.is_enabled === undefined ? null : channel.is_enabled ? ( + Active + ) : ( + Muted + )} + + ), + }, + ]; return ( <> - {showActionsInHeader ? ( - <> - + {!getUseUpdatedUx() && ( +
+ +

{channel?.name || '-'}

+
+ + + {channel?.is_enabled === undefined ? null : channel.is_enabled ? ( + Active + ) : ( + Muted + )} + + + + {channel && ( + - ), - }, - { - renderComponent: ( -
- - Send test message - -
- ), - }, - ]} - bodyStyles={{ padding: 'initial' }} - title="Channel Details" - titleSize="m" - /> - - - {channel?.is_enabled === undefined ? null : channel.is_enabled ? ( - Active - ) : ( - Muted - )} -
- ), - }, - ]} - bodyStyles={{ padding: 'initial' }} - title="Channel Details" - titleSize="m" - /> - - ) : ( -
- -

{channel?.name || '-'}

-
- - - {channel?.is_enabled === undefined ? null : channel.is_enabled ? ( - Active - ) : ( - Muted - )} - - - - {channel && ( - - )} - - - {channel && ( - - {({ onShow }) => ( - { - if (channel.is_enabled) { - onShow(MuteChannelModal, { - selected: [channel], - setSelected: (selected: React.SetStateAction[]) => 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 && ( + + {({ onShow }) => ( + { + if (channel.is_enabled) { + onShow(MuteChannelModal, { + selected: [channel], + setSelected: (selected: React.SetStateAction[]) => setChannel(selected[0]), }); - } - }} - > - {channel.is_enabled ? 'Mute channel' : 'Unmute channel'} - - )} - - )} - - -
- )} + } 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'} + + )} + + )} +
+
+
+ )} + ); - }; diff --git a/public/pages/Channels/components/details/ChannelDetailsActions.tsx b/public/pages/Channels/components/details/ChannelDetailsActions.tsx index c5bb359d..537b8fc1 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; @@ -30,7 +31,6 @@ interface ChannelDetailsActionsParams { interface ChannelDetailsActionsProps { channel: ChannelItemType; - showActionsInHeader: Boolean; } export function ChannelDetailsActions(props: ChannelDetailsActionsProps) { @@ -69,8 +69,8 @@ export function ChannelDetailsActions(props: ChannelDetailsActionsProps) { }, ]; - // Add Send Test Message action only if showActionsInHeader is false - if (!props.showActionsInHeader) { + // Add Send Test Message action only if getUseUpdatedUx is false + if (!getUseUpdatedUx()) { actions.splice(1, 0, { label: 'Send test message', disabled: diff --git a/public/pages/CreateChannel/CreateChannel.tsx b/public/pages/CreateChannel/CreateChannel.tsx index fe589f2c..0927c880 100644 --- a/public/pages/CreateChannel/CreateChannel.tsx +++ b/public/pages/CreateChannel/CreateChannel.tsx @@ -28,6 +28,7 @@ import { BREADCRUMBS, CUSTOM_WEBHOOK_ENDPOINT_TYPE, ROUTES, + setBreadcrumbs, } from '../../utils/constants'; import { BACKEND_CHANNEL_TYPE, CHANNEL_TYPE } from '../../../common/constants' import { getErrorMessage } from '../../utils/helpers'; @@ -55,14 +56,9 @@ import { validateRecipients, validateWebhookURL, } from './utils/validationHelper'; -import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; -import { ApplicationStart } from 'opensearch-dashboards/public'; - +import { getUseUpdatedUx } from '../../services/utils/constants'; interface CreateChannelsProps extends RouteComponentProps<{ id?: string }> { edit?: boolean; - navigationUI: NavigationPublicPluginStart['ui']; - showActionsInHeader: boolean; - application: ApplicationStart; } type InputErrorsType = { [key: string]: string[] }; @@ -147,18 +143,10 @@ export function CreateChannel(props: CreateChannelsProps) { roleArn: [], }); - const navigationUI = props?.navigationUI || {}; - const { HeaderControl } = navigationUI; - const showActionsInHeader = props.showActionsInHeader; - const appllication = props?.application || {}; - const { setAppDescriptionControls } = appllication; - 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) { @@ -376,12 +364,7 @@ export function CreateChannel(props: CreateChannelsProps) { - {showActionsInHeader ? ( - - ) : ( + {!getUseUpdatedUx() && (

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

@@ -433,7 +416,7 @@ export function CreateChannel(props: CreateChannelsProps) { microsoftTeamsWebhook={microsoftTeamsWebhook} setMicrosoftTeamsWebhook={setMicrosoftTeamsWebhook} /> - ) : channelType === BACKEND_CHANNEL_TYPE.EMAIL ? ( + ): channelType === BACKEND_CHANNEL_TYPE.EMAIL ? ( { coreContext.notifications.toasts.addSuccess( - `Channel ${name} successfully ${props.edit ? 'updated' : 'created' + `Channel ${name} successfully ${ + props.edit ? 'updated' : 'created' }.` ); setTimeout(() => (location.hash = prevURL), SERVER_DELAY); @@ -531,8 +515,9 @@ export function CreateChannel(props: CreateChannelsProps) { coreContext.notifications.toasts.addError( error?.body || error, { - title: `Failed to ${props.edit ? 'update' : 'create' - } channel.`, + title: `Failed to ${ + props.edit ? 'update' : 'create' + } channel.`, } ); }); @@ -546,4 +531,3 @@ export function CreateChannel(props: CreateChannelsProps) { ); } - diff --git a/public/pages/Emails/CreateRecipientGroup.tsx b/public/pages/Emails/CreateRecipientGroup.tsx index d566f7df..dd4f9758 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,15 +26,11 @@ import { validateRecipientGroupEmails, validateRecipientGroupName, } from './utils/validationHelper'; -import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; -import { ApplicationStart } from 'opensearch-dashboards/public'; +import { getUseUpdatedUx } from '../../services/utils/constants'; interface CreateRecipientGroupProps extends RouteComponentProps<{ id?: string }> { edit?: boolean; - navigationUI: NavigationPublicPluginStart['ui']; - showActionsInHeader: boolean; - application: ApplicationStart; } export function CreateRecipientGroup(props: CreateRecipientGroupProps) { @@ -70,7 +66,7 @@ export function CreateRecipientGroup(props: CreateRecipientGroupProps) { }; useEffect(() => { - coreContext.chrome.setBreadcrumbs([ + setBreadcrumbs([ BREADCRUMBS.NOTIFICATIONS, BREADCRUMBS.EMAIL_GROUPS, props.edit @@ -108,7 +104,7 @@ export function CreateRecipientGroup(props: CreateRecipientGroupProps) { return ( <> - {!props.showActionsInHeader && ( + {!getUseUpdatedUx() && (

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

diff --git a/public/pages/Emails/CreateSESSender.tsx b/public/pages/Emails/CreateSESSender.tsx index 42dbd9e7..7e641913 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,14 +28,9 @@ import { validateRoleArn, validateSenderName, } from './utils/validationHelper'; -import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; -import { ApplicationStart } from 'opensearch-dashboards/public'; - +import { getUseUpdatedUx } from '../../services/utils/constants'; interface CreateSESSenderProps extends RouteComponentProps<{ id?: string }> { edit?: boolean; - navigationUI: NavigationPublicPluginStart['ui']; - showActionsInHeader: boolean; - application: ApplicationStart; } export function CreateSESSender(props: CreateSESSenderProps) { @@ -56,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, @@ -106,7 +101,7 @@ export function CreateSESSender(props: CreateSESSenderProps) { return ( <> - {!props.showActionsInHeader && ( + {!getUseUpdatedUx() && (

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

diff --git a/public/pages/Emails/CreateSender.tsx b/public/pages/Emails/CreateSender.tsx index 023fec34..368cf182 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,14 +27,9 @@ import { validatePort, validateSenderName, } from './utils/validationHelper'; -import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; -import { ApplicationStart } from 'opensearch-dashboards/public'; - +import { getUseUpdatedUx } from '../../services/utils/constants'; interface CreateSenderProps extends RouteComponentProps<{ id?: string }> { edit?: boolean; - navigationUI: NavigationPublicPluginStart['ui']; - showActionsInHeader: boolean; - application: ApplicationStart; } export function CreateSender(props: CreateSenderProps) { @@ -57,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, @@ -103,7 +98,7 @@ export function CreateSender(props: CreateSenderProps) { return ( <> - {!props.showActionsInHeader && ( + {!getUseUpdatedUx() && (

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

diff --git a/public/pages/Emails/EmailGroups.tsx b/public/pages/Emails/EmailGroups.tsx index 7d223cb5..3770f2df 100644 --- a/public/pages/Emails/EmailGroups.tsx +++ b/public/pages/Emails/EmailGroups.tsx @@ -10,14 +10,10 @@ import { CoreServicesContext } from '../../components/coreServices'; import { BREADCRUMBS } from '../../utils/constants'; import { RecipientGroupsTable } from './components/tables/RecipientGroupsTable'; import { NotificationService } from '../../services'; -import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; -import { ApplicationStart } from 'opensearch-dashboards/public'; +import { getUseUpdatedUx } from '../../services/utils/constants'; interface EmailGroupsProps extends RouteComponentProps { notificationService: NotificationService; - navigationUI: NavigationPublicPluginStart['ui']; - showActionsInHeader: boolean; - application: ApplicationStart; } export function EmailGroups(props: EmailGroupsProps) { @@ -32,7 +28,7 @@ export function EmailGroups(props: EmailGroupsProps) { return ( <> - {!props.showActionsInHeader && ( + {!getUseUpdatedUx() && (

Email recipient groups

@@ -41,9 +37,6 @@ export function EmailGroups(props: EmailGroupsProps) { ); diff --git a/public/pages/Emails/EmailSenders.tsx b/public/pages/Emails/EmailSenders.tsx index 0fbcd536..b7da4ddd 100644 --- a/public/pages/Emails/EmailSenders.tsx +++ b/public/pages/Emails/EmailSenders.tsx @@ -7,19 +7,15 @@ 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 { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; -import { ApplicationStart } from 'opensearch-dashboards/public'; +import { getUseUpdatedUx } from '../../services/utils/constants'; interface EmailSendersProps extends RouteComponentProps { notificationService: NotificationService; - navigationUI: NavigationPublicPluginStart['ui']; - showActionsInHeader: boolean; - application: ApplicationStart; } export function EmailSenders(props: EmailSendersProps) { @@ -28,18 +24,16 @@ export function EmailSenders(props: EmailSendersProps) { useEffect(() => { - coreContext.chrome.setBreadcrumbs([ + setBreadcrumbs([ BREADCRUMBS.NOTIFICATIONS, BREADCRUMBS.EMAIL_SENDERS, ]); window.scrollTo(0, 0); }, []); - const showActionsInHeader = props.showActionsInHeader; - return ( <> - {!showActionsInHeader && ( + {!getUseUpdatedUx() && (

Email senders

@@ -49,9 +43,6 @@ export function EmailSenders(props: EmailSendersProps) { )} @@ -62,9 +53,6 @@ export function EmailSenders(props: EmailSendersProps) { )} 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__/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__/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/components/tables/RecipientGroupsTable.tsx b/public/pages/Emails/components/tables/RecipientGroupsTable.tsx index 1da1a7e9..31c4af49 100644 --- a/public/pages/Emails/components/tables/RecipientGroupsTable.tsx +++ b/public/pages/Emails/components/tables/RecipientGroupsTable.tsx @@ -43,14 +43,13 @@ import { isDataSourceError, isDataSourceChanged, } from '../../../../components/MDSEnabledComponent/MDSEnabledComponent'; -import { NavigationPublicPluginStart, TopNavControlButtonData, TopNavControlTextData } from 'src/plugins/navigation/public'; +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; notificationService: NotificationService; - navigationUI: NavigationPublicPluginStart['ui']; - showActionsInHeader: boolean; - application: ApplicationStart; } interface RecipientGroupsTableState @@ -233,12 +232,6 @@ export class RecipientGroupsTable extends Component< onSelectionChange: this.onSelectionChange, }; - const navigationUI = this.props?.navigationUI || {}; - const { HeaderControl } = navigationUI; - const showActionsInHeader = this.props.showActionsInHeader; - const appllication = this.props?.application || {}; - const { setAppRightControls, setAppLeftControls } = appllication; - const actions = [ { label: 'Edit', @@ -278,7 +271,7 @@ export class RecipientGroupsTable extends Component< return ( <> - {showActionsInHeader ? ( + {getUseUpdatedUx() ? ( - ), - }, - { - component: ( - - ) + ), } ]} /> diff --git a/public/pages/Emails/components/tables/SESSendersTable.tsx b/public/pages/Emails/components/tables/SESSendersTable.tsx index fbdb568c..156a741b 100644 --- a/public/pages/Emails/components/tables/SESSendersTable.tsx +++ b/public/pages/Emails/components/tables/SESSendersTable.tsx @@ -13,7 +13,6 @@ import { EuiFlexItem, EuiHorizontalRule, EuiPopover, - EuiSelect, EuiTableFieldDataColumnType, EuiTableSortingType, SortDirection, @@ -21,8 +20,8 @@ import { 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, useState } from 'react'; -import { ApplicationStart, CoreStart } from '../../../../../../../src/core/public'; +import React, { Component } from 'react'; +import { CoreStart } from '../../../../../../../src/core/public'; import { SESSenderItemType, TableState, @@ -40,14 +39,11 @@ import { isDataSourceError, isDataSourceChanged, } from '../../../../components/MDSEnabledComponent/MDSEnabledComponent'; -import { NavigationPublicPluginStart, TopNavControlButtonData } from 'src/plugins/navigation/public'; +import { getUseUpdatedUx } from '../../../../../public/services/utils/constants'; interface SESSendersTableProps { coreContext: CoreStart; notificationService: NotificationService; - navigationUI: NavigationPublicPluginStart['ui']; - showActionsInHeader: boolean; - application: ApplicationStart; } interface SESSendersTableState extends TableState { } @@ -197,8 +193,6 @@ export class SESSendersTable extends Component< onSelectionChange: this.onSelectionChange, }; - const showActionsInHeader = this.props.showActionsInHeader; - const actions = [ { label: 'Edit', @@ -220,7 +214,7 @@ export class SESSendersTable extends Component< return ( <> - {showActionsInHeader ? ( + {getUseUpdatedUx() ? ( { @@ -219,8 +216,6 @@ export class SendersTable extends Component< onSelectionChange: this.onSelectionChange, }; - const showActionsInHeader = this.props.showActionsInHeader; - const actions = [ { label: 'Edit', @@ -242,7 +237,7 @@ export class SendersTable extends Component< return ( <> - {showActionsInHeader ? ( + {getUseUpdatedUx() ? ( { @@ -104,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(); @@ -247,13 +244,6 @@ export default class Main extends Component { services && ( - { render={(routeProps: RouteComponentProps) => ( )} /> @@ -385,9 +372,6 @@ export default class Main extends Component { )} /> @@ -396,9 +380,6 @@ export default class Main extends Component { render={(routeProps: RouteComponentProps<{ id: string }>) => ( )} /> @@ -408,9 +389,6 @@ export default class Main extends Component { )} /> @@ -420,9 +398,6 @@ export default class Main extends Component { )} /> @@ -432,9 +407,6 @@ export default class Main extends Component { )} /> @@ -443,9 +415,6 @@ export default class Main extends Component { render={(routeProps: RouteComponentProps) => ( )} /> @@ -455,9 +424,6 @@ export default class Main extends Component { )} /> @@ -466,9 +432,6 @@ export default class Main extends Component { render={(routeProps: RouteComponentProps) => ( )} /> @@ -478,9 +441,6 @@ export default class Main extends Component { )} /> @@ -489,9 +449,6 @@ export default class Main extends Component { render={(routeProps: RouteComponentProps) => ( )} /> @@ -501,9 +458,6 @@ export default class Main extends Component { )} /> @@ -514,7 +468,6 @@ export default class Main extends Component { )} - ) 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/NavigationContext.ts b/public/services/NavigationContext.ts deleted file mode 100644 index a3e16fb7..00000000 --- a/public/services/NavigationContext.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { ApplicationStart } from "opensearch-dashboards/public"; -import { createContext } from "react"; -import { NavigationPublicPluginStart } from "src/plugins/navigation/public"; - -export interface NavigationMenuProperties { - navigationUI: NavigationPublicPluginStart['ui']; - showActionsInHeader: boolean; - application: ApplicationStart; -} - -const NavigationMenuContext = createContext({ - navigationUI: {} as NavigationPublicPluginStart['ui'], - showActionsInHeader: false, - application: {} as ApplicationStart, -}); - -const NavigationMenuConsumer = NavigationMenuContext.Consumer; - -export { NavigationMenuContext, NavigationMenuConsumer }; 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 cf407306..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'; @@ -27,7 +30,7 @@ export const BREADCRUMBS = Object.freeze({ NOTIFICATIONS: { text: 'Notification channels', href: '#/' }, CHANNELS: { text: 'Channels', href: `#${ROUTES.CHANNELS}` }, CHANNEL_DETAILS: { text: 'Channels', href: `#${ROUTES.CHANNEL_DETAILS}` }, - CREATE_CHANNEL: { text: '', href: `#${ROUTES.CREATE_CHANNEL}` }, + CREATE_CHANNEL: { text: 'Create channel', href: `#${ROUTES.CREATE_CHANNEL}` }, EDIT_CHANNEL: { text: 'Edit channel' }, EMAIL_SENDERS: { text: 'Email senders', href: `#${ROUTES.EMAIL_SENDERS}` }, EMAIL_GROUPS: { text: 'Email recipient groups', href: `#${ROUTES.EMAIL_GROUPS}` }, @@ -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/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 From 5bbf306d4a567c991a39f3b83c1a648ea5308e3d Mon Sep 17 00:00:00 2001 From: Riya Saxena Date: Thu, 15 Aug 2024 17:48:57 -0700 Subject: [PATCH 12/21] fix UTs Signed-off-by: Riya Saxena --- .babelrc | 18 - babel.config.js | 21 + package.json | 9 +- .../__tests__/ChannelDetails.test.tsx | 5 + .../__tests__/ChannelDetailsActions.test.tsx | 5 + .../Channels/__tests__/Channels.test.tsx | 5 + .../__tests__/CreateChannel.test.tsx | 5 + .../__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 + .../Emails/__tests__/EmailGroups.test.tsx | 7 +- .../__tests__/RecipientGroupsTable.test.tsx | 5 + .../Emails/__tests__/SendersTable.test.tsx | 5 + .../Emails/__tests__/validationHelper.test.ts | 5 + public/pages/Main/__tests__/Main.test.tsx | 6 +- test/jest.config.js | 2 +- test/polyfills.ts | 9 + test/polyfills/mutationObserver.js | 572 ++++++++++++++++++ test/setup.jest.ts | 9 +- yarn.lock | 136 ++--- 22 files changed, 753 insertions(+), 96 deletions(-) delete mode 100644 .babelrc create mode 100644 babel.config.js create mode 100644 test/polyfills.ts create mode 100644 test/polyfills/mutationObserver.js 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/package.json b/package.json index 2704d818..2e0edd9b 100644 --- a/package.json +++ b/package.json @@ -20,13 +20,16 @@ "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": {}, + "dependencies": { + "tough-cookie": "^4.1.3", + "web-streams-polyfill": "^4.0.0" + }, "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/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/CreateChannel/__tests__/CreateChannel.test.tsx b/public/pages/CreateChannel/__tests__/CreateChannel.test.tsx index b78bc44e..26552466 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/__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__/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__/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__/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/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/test/jest.config.js b/test/jest.config.js index a88c0ff5..b1121f39 100644 --- a/test/jest.config.js +++ b/test/jest.config.js @@ -6,7 +6,7 @@ module.exports = { rootDir: '../', setupFiles: ['/test/setupTests.ts'], - setupFilesAfterEnv: ['/test/setup.jest.ts'], + setupFilesAfterEnv: ['/test/setup.jest.ts', '/test/polyfills.ts'], roots: [''], testMatch: ['**/*.test.js', '**/*.test.jsx', '**/*.test.ts', '**/*.test.tsx'], clearMocks: true, diff --git a/test/polyfills.ts b/test/polyfills.ts new file mode 100644 index 00000000..e39895c7 --- /dev/null +++ b/test/polyfills.ts @@ -0,0 +1,9 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +// @ts-ignore +import { MutationObserver } from './polyfills/mutationObserver'; + +Object.defineProperty(window, 'MutationObserver', { value: MutationObserver }); \ No newline at end of file diff --git a/test/polyfills/mutationObserver.js b/test/polyfills/mutationObserver.js new file mode 100644 index 00000000..53709e32 --- /dev/null +++ b/test/polyfills/mutationObserver.js @@ -0,0 +1,572 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +/* eslint-disable */ +// transpiled typescript->javascript from +// https://github.com/aurelia/pal-nodejs/blob/master/src/polyfills/mutation-observer.ts + +/* + * Based on Shim for MutationObserver interface + * Author: Graeme Yeates (github.com/megawac) + * Repository: https://github.com/megawac/MutationObserver.js + */ +import { EventEmitter } from 'events'; + +var __extends = + (this && this.__extends) || + (function () { + var extendStatics = + Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && + function (d, b) { + d.__proto__ = b; + }) || + function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + }; + return function (d, b) { + extendStatics(d, b); + function __() { + this.constructor = d; + } + d.prototype = b === null ? Object.create(b) : ((__.prototype = b.prototype), new __()); + }; + })(); + +module.exports = {}; + +Object.defineProperty(module.exports, '__esModule', { value: true }); +var Util = /** @class */ (function () { + function Util() {} + Util.clone = function ($target, config) { + var recurse = true; // set true so childList we'll always check the first level + return (function copy($target) { + var elestruct = { + /** @type {Node} */ + node: $target, + charData: null, + attr: null, + kids: null, + }; + // Store current character data of target text or comment node if the config requests + // those properties to be observed. + if (config.charData && ($target.nodeType === 3 || $target.nodeType === 8)) { + elestruct.charData = $target.nodeValue; + } else { + // Add attr only if subtree is specified or top level and avoid if + // attributes is a document object (#13). + if (config.attr && recurse && $target.nodeType === 1) { + /** + * clone live attribute list to an object structure {name: val} + * @type {Object.} + */ + elestruct.attr = Util.reduce( + $target.attributes, + function (memo, attr) { + if (!config.afilter || config.afilter[attr.name]) { + memo[attr.name] = attr.value; + } + return memo; + }, + {} + ); + } + // whether we should iterate the children of $target node + if (recurse && (config.kids || config.charData || (config.attr && config.descendents))) { + /** @type {Array.} : Array of custom clone */ + elestruct.kids = Util.map($target.childNodes, copy); + } + recurse = config.descendents; + } + return elestruct; + })($target); + }; + /** + * indexOf an element in a collection of custom nodes + * + * @param {NodeList} set + * @param {!Object} $node : A custom cloned nodeg333 + * @param {number} idx : index to start the loop + * @return {number} + */ + Util.indexOfCustomNode = function (set, $node, idx) { + var JSCompiler_renameProperty = function (a) { + return a; + }; + return this.indexOf(set, $node, idx, JSCompiler_renameProperty('node')); + }; + /** + * Attempt to uniquely id an element for hashing. We could optimize this for legacy browsers but it hopefully wont be called enough to be a concern + * + * @param {Node} $ele + * @return {(string|number)} + */ + Util.getElementId = function ($ele) { + try { + return $ele.id || ($ele[this.expando] = $ele[this.expando] || this.counter++); + } catch (e) { + // ie <8 will throw if you set an unknown property on a text node + try { + return $ele.nodeValue; // naive + } catch (shitie) { + // when text node is removed: https://gist.github.com/megawac/8355978 :( + return this.counter++; + } + } + }; + /** + * **map** Apply a mapping function to each item of a set + * @param {Array|NodeList} set + * @param {Function} iterator + */ + Util.map = function (set, iterator) { + var results = []; + for (var index = 0; index < set.length; index++) { + results[index] = iterator(set[index], index, set); + } + return results; + }; + /** + * **Reduce** builds up a single result from a list of values + * @param {Array|NodeList|NamedNodeMap} set + * @param {Function} iterator + * @param {*} [memo] Initial value of the memo. + */ + Util.reduce = function (set, iterator, memo) { + for (var index = 0; index < set.length; index++) { + memo = iterator(memo, set[index], index, set); + } + return memo; + }; + /** + * **indexOf** find index of item in collection. + * @param {Array|NodeList} set + * @param {Object} item + * @param {number} idx + * @param {string} [prop] Property on set item to compare to item + */ + Util.indexOf = function (set, item, idx, prop) { + for (; /*idx = ~~idx*/ idx < set.length; idx++) { + // start idx is always given as this is internal + if ((prop ? set[idx][prop] : set[idx]) === item) return idx; + } + return -1; + }; + /** + * @param {Object} obj + * @param {(string|number)} prop + * @return {boolean} + */ + Util.has = function (obj, prop) { + return obj[prop] !== undefined; // will be nicely inlined by gcc + }; + Util.counter = 1; + Util.expando = 'mo_id'; + return Util; +})(); +module.exports.Util = Util; +var MutationObserver = /** @class */ (function () { + function MutationObserver(listener) { + var _this = this; + this._watched = []; + this._listener = null; + this._period = 30; + this._timeout = null; + this._disposed = false; + this._notifyListener = null; + this._watched = []; + this._listener = listener; + this._period = 30; + this._notifyListener = function () { + _this.scheduleMutationCheck(_this); + }; + } + MutationObserver.prototype.observe = function ($target, config) { + var settings = { + attr: !!(config.attributes || config.attributeFilter || config.attributeOldValue), + // some browsers enforce that subtree must be set with childList, attributes or characterData. + // We don't care as spec doesn't specify this rule. + kids: !!config.childList, + descendents: !!config.subtree, + charData: !!(config.characterData || config.characterDataOldValue), + afilter: null, + }; + MutationNotifier.getInstance().on('changed', this._notifyListener); + var watched = this._watched; + // remove already observed target element from pool + for (var i = 0; i < watched.length; i++) { + if (watched[i].tar === $target) watched.splice(i, 1); + } + if (config.attributeFilter) { + /** + * converts to a {key: true} dict for faster lookup + * @type {Object.} + */ + settings.afilter = Util.reduce( + config.attributeFilter, + function (a, b) { + a[b] = true; + return a; + }, + {} + ); + } + watched.push({ + tar: $target, + fn: this.createMutationSearcher($target, settings), + }); + }; + MutationObserver.prototype.takeRecords = function () { + var mutations = []; + var watched = this._watched; + for (var i = 0; i < watched.length; i++) { + watched[i].fn(mutations); + } + return mutations; + }; + MutationObserver.prototype.disconnect = function () { + this._watched = []; // clear the stuff being observed + MutationNotifier.getInstance().removeListener('changed', this._notifyListener); + this._disposed = true; + clearTimeout(this._timeout); // ready for garbage collection + this._timeout = null; + }; + MutationObserver.prototype.createMutationSearcher = function ($target, config) { + var _this = this; + /** type {Elestuct} */ + var $oldstate = Util.clone($target, config); // create the cloned datastructure + /** + * consumes array of mutations we can push to + * + * @param {Array.} mutations + */ + return function (mutations) { + var olen = mutations.length; + var dirty; + if (config.charData && $target.nodeType === 3 && $target.nodeValue !== $oldstate.charData) { + mutations.push( + new MutationRecord({ + type: 'characterData', + target: $target, + oldValue: $oldstate.charData, + }) + ); + } + // Alright we check base level changes in attributes... easy + if (config.attr && $oldstate.attr) { + _this.findAttributeMutations(mutations, $target, $oldstate.attr, config.afilter); + } + // check childlist or subtree for mutations + if (config.kids || config.descendents) { + dirty = _this.searchSubtree(mutations, $target, $oldstate, config); + } + // reclone data structure if theres changes + if (dirty || mutations.length !== olen) { + /** type {Elestuct} */ + $oldstate = Util.clone($target, config); + } + }; + }; + MutationObserver.prototype.scheduleMutationCheck = function (observer) { + var _this = this; + // Only schedule if there isn't already a timer. + if (!observer._timeout) { + observer._timeout = setTimeout(function () { + return _this.mutationChecker(observer); + }, this._period); + } + }; + MutationObserver.prototype.mutationChecker = function (observer) { + // allow scheduling a new timer. + observer._timeout = null; + var mutations = observer.takeRecords(); + if (mutations.length) { + // fire away + // calling the listener with context is not spec but currently consistent with FF and WebKit + observer._listener(mutations, observer); + } + }; + MutationObserver.prototype.searchSubtree = function (mutations, $target, $oldstate, config) { + var _this = this; + // Track if the tree is dirty and has to be recomputed (#14). + var dirty; + /* + * Helper to identify node rearrangment and stuff... + * There is no gaurentee that the same node will be identified for both added and removed nodes + * if the positions have been shuffled. + * conflicts array will be emptied by end of operation + */ + var _resolveConflicts = function (conflicts, node, $kids, $oldkids, numAddedNodes) { + // the distance between the first conflicting node and the last + var distance = conflicts.length - 1; + // prevents same conflict being resolved twice consider when two nodes switch places. + // only one should be given a mutation event (note -~ is used as a math.ceil shorthand) + var counter = -~((distance - numAddedNodes) / 2); + var $cur; + var oldstruct; + var conflict; + while ((conflict = conflicts.pop())) { + $cur = $kids[conflict.i]; + oldstruct = $oldkids[conflict.j]; + // attempt to determine if there was node rearrangement... won't gaurentee all matches + // also handles case where added/removed nodes cause nodes to be identified as conflicts + if (config.kids && counter && Math.abs(conflict.i - conflict.j) >= distance) { + mutations.push( + new MutationRecord({ + type: 'childList', + target: node, + addedNodes: [$cur], + removedNodes: [$cur], + // haha don't rely on this please + nextSibling: $cur.nextSibling, + previousSibling: $cur.previousSibling, + }) + ); + counter--; // found conflict + } + // Alright we found the resorted nodes now check for other types of mutations + if (config.attr && oldstruct.attr) + _this.findAttributeMutations(mutations, $cur, oldstruct.attr, config.afilter); + if (config.charData && $cur.nodeType === 3 && $cur.nodeValue !== oldstruct.charData) { + mutations.push( + new MutationRecord({ + type: 'characterData', + target: $cur, + oldValue: oldstruct.charData, + }) + ); + } + // now look @ subtree + if (config.descendents) _findMutations($cur, oldstruct); + } + }; + /** + * Main worker. Finds and adds mutations if there are any + * @param {Node} node + * @param {!Object} old : A cloned data structure using internal clone + */ + var _findMutations = function (node, old) { + var $kids = node.childNodes; + var $oldkids = old.kids; + var klen = $kids.length; + // $oldkids will be undefined for text and comment nodes + var olen = $oldkids ? $oldkids.length : 0; + // if (!olen && !klen) return; // both empty; clearly no changes + // we delay the intialization of these for marginal performance in the expected case (actually quite signficant on large subtrees when these would be otherwise unused) + // map of checked element of ids to prevent registering the same conflict twice + var map; + // array of potential conflicts (ie nodes that may have been re arranged) + var conflicts; + var id; // element id from getElementId helper + var idx; // index of a moved or inserted element + var oldstruct; + // current and old nodes + var $cur; + var $old; + // track the number of added nodes so we can resolve conflicts more accurately + var numAddedNodes = 0; + // iterate over both old and current child nodes at the same time + var i = 0; + var j = 0; + // while there is still anything left in $kids or $oldkids (same as i < $kids.length || j < $oldkids.length;) + while (i < klen || j < olen) { + // current and old nodes at the indexs + $cur = $kids[i]; + oldstruct = $oldkids[j]; + $old = oldstruct && oldstruct.node; + if ($cur === $old) { + // expected case - optimized for this case + // check attributes as specified by config + if (config.attr && oldstruct.attr) { + /* oldstruct.attr instead of textnode check */ + _this.findAttributeMutations(mutations, $cur, oldstruct.attr, config.afilter); + } + // check character data if node is a comment or textNode and it's being observed + if ( + config.charData && + oldstruct.charData !== undefined && + $cur.nodeValue !== oldstruct.charData + ) { + mutations.push( + new MutationRecord({ + type: 'characterData', + target: $cur, + }) + ); + } + // resolve conflicts; it will be undefined if there are no conflicts - otherwise an array + if (conflicts) _resolveConflicts(conflicts, node, $kids, $oldkids, numAddedNodes); + // recurse on next level of children. Avoids the recursive call when there are no children left to iterate + if ( + config.descendents && + ($cur.childNodes.length || (oldstruct.kids && oldstruct.kids.length)) + ) + _findMutations($cur, oldstruct); + i++; + j++; + } else { + // (uncommon case) lookahead until they are the same again or the end of children + dirty = true; + if (!map) { + // delayed initalization (big perf benefit) + map = {}; + conflicts = []; + } + if ($cur) { + // check id is in the location map otherwise do a indexOf search + if (!map[(id = Util.getElementId($cur))]) { + // to prevent double checking + // mark id as found + map[id] = true; + // custom indexOf using comparitor checking oldkids[i].node === $cur + if ((idx = Util.indexOfCustomNode($oldkids, $cur, j)) === -1) { + if (config.kids) { + mutations.push( + new MutationRecord({ + type: 'childList', + target: node, + addedNodes: [$cur], + nextSibling: $cur.nextSibling, + previousSibling: $cur.previousSibling, + }) + ); + numAddedNodes++; + } + } else { + conflicts.push({ + i: i, + j: idx, + }); + } + } + i++; + } + if ( + $old && + // special case: the changes may have been resolved: i and j appear congurent so we can continue using the expected case + $old !== $kids[i] + ) { + if (!map[(id = Util.getElementId($old))]) { + map[id] = true; + if ((idx = Util.indexOf($kids, $old, i)) === -1) { + if (config.kids) { + mutations.push( + new MutationRecord({ + type: 'childList', + target: old.node, + removedNodes: [$old], + nextSibling: $oldkids[j + 1], + previousSibling: $oldkids[j - 1], + }) + ); + numAddedNodes--; + } + } else { + conflicts.push({ + i: idx, + j: j, + }); + } + } + j++; + } + } // end uncommon case + } // end loop + // resolve any remaining conflicts + if (conflicts) _resolveConflicts(conflicts, node, $kids, $oldkids, numAddedNodes); + }; + _findMutations($target, $oldstate); + return dirty; + }; + MutationObserver.prototype.findAttributeMutations = function ( + mutations, + $target, + $oldstate, + filter + ) { + var checked = {}; + var attributes = $target.attributes; + var attr; + var name; + var i = attributes.length; + while (i--) { + attr = attributes[i]; + name = attr.name; + if (!filter || Util.has(filter, name)) { + if (attr.value !== $oldstate[name]) { + // The pushing is redundant but gzips very nicely + mutations.push( + new MutationRecord({ + type: 'attributes', + target: $target, + attributeName: name, + oldValue: $oldstate[name], + attributeNamespace: attr.namespaceURI, // in ie<8 it incorrectly will return undefined + }) + ); + } + checked[name] = true; + } + } + for (name in $oldstate) { + if (!checked[name]) { + mutations.push( + new MutationRecord({ + target: $target, + type: 'attributes', + attributeName: name, + oldValue: $oldstate[name], + }) + ); + } + } + }; + return MutationObserver; +})(); +module.exports.MutationObserver = MutationObserver; +var MutationRecord = /** @class */ (function () { + function MutationRecord(data) { + var settings = { + type: null, + target: null, + addedNodes: [], + removedNodes: [], + previousSibling: null, + nextSibling: null, + attributeName: null, + attributeNamespace: null, + oldValue: null, + }; + for (var prop in data) { + if (Util.has(settings, prop) && data[prop] !== undefined) settings[prop] = data[prop]; + } + return settings; + } + return MutationRecord; +})(); +module.exports.MutationRecord = MutationRecord; +var MutationNotifier = /** @class */ (function (_super) { + __extends(MutationNotifier, _super); + function MutationNotifier() { + var _this = _super.call(this) || this; + _this.setMaxListeners(100); + return _this; + } + MutationNotifier.getInstance = function () { + if (!MutationNotifier._instance) { + MutationNotifier._instance = new MutationNotifier(); + } + return MutationNotifier._instance; + }; + MutationNotifier.prototype.destruct = function () { + this.removeAllListeners('changed'); + }; + MutationNotifier.prototype.notifyChanged = function (node) { + this.emit('changed', node); + }; + MutationNotifier._instance = null; + return MutationNotifier; +})(EventEmitter); +module.exports.MutationNotifier = MutationNotifier; \ No newline at end of file diff --git a/test/setup.jest.ts b/test/setup.jest.ts index e7c006b6..7aa54103 100644 --- a/test/setup.jest.ts +++ b/test/setup.jest.ts @@ -3,7 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -// import '@testing-library/jest-dom/extend-expect'; +import '@testing-library/jest-dom/extend-expect'; +import { ReadableStream } from 'web-streams-polyfill'; // Polyfill for ReadableStream +import { TextDecoder, TextEncoder } from 'util'; +global.TextEncoder = TextEncoder; +// @ts-expect-error +global.TextDecoder = TextDecoder; +// @ts-expect-error +global.ReadableStream = ReadableStream; import { configure } from '@testing-library/react'; configure({ testIdAttribute: 'data-test-subj' }); 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" From ff8dd09d42423b5fb2f23f7a878ca1366cc93706 Mon Sep 17 00:00:00 2001 From: Riya Saxena Date: Mon, 19 Aug 2024 09:41:33 -0700 Subject: [PATCH 13/21] Compressed UX Signed-off-by: Riya Saxena --- public/pages/Channels/Channels.tsx | 14 ++++---- .../components/details/ChannelDetails.tsx | 9 ++--- .../tables/RecipientGroupsTable.tsx | 32 ++++++++--------- .../components/tables/SESSendersTable.tsx | 36 +++++++++---------- .../Emails/components/tables/SendersTable.tsx | 31 ++++++++-------- 5 files changed, 61 insertions(+), 61 deletions(-) diff --git a/public/pages/Channels/Channels.tsx b/public/pages/Channels/Channels.tsx index 53a157ed..376558b6 100644 --- a/public/pages/Channels/Channels.tsx +++ b/public/pages/Channels/Channels.tsx @@ -5,7 +5,7 @@ import { EuiBasicTable, - EuiButton, + EuiSmallButton, EuiEmptyPrompt, EuiHealth, EuiHorizontalRule, @@ -293,9 +293,9 @@ export class Channels extends MDSEnabledComponent title={

No channels to display

} body="To send or receive notifications, you will need to create a notification channel." actions={ - + Create channel - + } /> } @@ -330,9 +330,9 @@ export class Channels extends MDSEnabledComponent }, { component: ( - + Create channel - + ), }, ]} @@ -361,9 +361,9 @@ export class Channels extends MDSEnabledComponent title={

No channels to display

} body="To send or receive notifications, you will need to create a notification channel." actions={ - + Create channel - + } /> } diff --git a/public/pages/Channels/components/details/ChannelDetails.tsx b/public/pages/Channels/components/details/ChannelDetails.tsx index d1ce6361..31c33a14 100644 --- a/public/pages/Channels/components/details/ChannelDetails.tsx +++ b/public/pages/Channels/components/details/ChannelDetails.tsx @@ -8,6 +8,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiHealth, + EuiSmallButton, EuiSpacer, EuiText, EuiTitle, @@ -56,7 +57,7 @@ const ChannelHeaderContent = ({ channel, setChannel }) => { {channel && ( {({ onShow }) => ( - { @@ -79,7 +80,7 @@ const ChannelHeaderContent = ({ channel, setChannel }) => { }} > {channel.is_enabled ? 'Mute channel' : 'Unmute channel'} - + )} )} @@ -264,7 +265,7 @@ export function ChannelDetails(props: ChannelDetailsProps) { {channel && ( {({ onShow }) => ( - { @@ -287,7 +288,7 @@ export function ChannelDetails(props: ChannelDetailsProps) { }} > {channel.is_enabled ? 'Mute channel' : 'Unmute channel'} - + )} )} diff --git a/public/pages/Emails/components/tables/RecipientGroupsTable.tsx b/public/pages/Emails/components/tables/RecipientGroupsTable.tsx index 31c4af49..ab8c0d63 100644 --- a/public/pages/Emails/components/tables/RecipientGroupsTable.tsx +++ b/public/pages/Emails/components/tables/RecipientGroupsTable.tsx @@ -5,10 +5,10 @@ import { EuiBasicTable, - EuiButton, + EuiSmallButton, EuiContextMenuItem, EuiEmptyPrompt, - EuiFieldSearch, + EuiCompressedFieldSearch, EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, @@ -293,7 +293,7 @@ export class RecipientGroupsTable extends Component< > - Actions - + } isOpen={this.state.isPopoverOpen} closePopover={() => this.setState({ isPopoverOpen: false })} @@ -354,9 +354,9 @@ export class RecipientGroupsTable extends Component< title={

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 - + } /> } @@ -375,7 +375,7 @@ export class RecipientGroupsTable extends Component< component: ( {({ onShow }) => ( - @@ -386,14 +386,14 @@ export class RecipientGroupsTable extends Component< } > Delete - + )} ), }, { component: ( - @@ -403,14 +403,14 @@ export class RecipientGroupsTable extends Component< } > Edit - + ), }, { component: ( - + Create recipient group - + ), }, ]} @@ -421,7 +421,7 @@ export class RecipientGroupsTable extends Component< 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 - + } /> } diff --git a/public/pages/Emails/components/tables/SESSendersTable.tsx b/public/pages/Emails/components/tables/SESSendersTable.tsx index 156a741b..2571ff5e 100644 --- a/public/pages/Emails/components/tables/SESSendersTable.tsx +++ b/public/pages/Emails/components/tables/SESSendersTable.tsx @@ -5,10 +5,10 @@ import { EuiBasicTable, - EuiButton, + EuiSmallButton, EuiContextMenuItem, EuiEmptyPrompt, - EuiFieldSearch, + EuiCompressedFieldSearch, EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, @@ -221,9 +221,9 @@ export class SESSendersTable extends Component< actions={[ { component: ( - + Create SES sender - + ), }, ]} @@ -236,7 +236,7 @@ export class SESSendersTable extends Component< > - Actions - + } isOpen={this.state.isPopoverOpen} closePopover={() => this.setState({ isPopoverOpen: false })} @@ -297,9 +297,9 @@ export class SESSendersTable extends Component< title={

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 - + } /> } @@ -319,7 +319,7 @@ export class SESSendersTable extends Component< component: ( {({ onShow }) => ( - @@ -330,14 +330,14 @@ export class SESSendersTable extends Component< } > Delete - + )} ), }, { component: ( - @@ -347,14 +347,14 @@ export class SESSendersTable extends Component< } > Edit - + ), }, { component: ( - + Create SES sender - + ), }, ]} @@ -365,7 +365,7 @@ export class SESSendersTable extends Component< titleSize="m" total={this.state.total} > - 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 - + } /> } diff --git a/public/pages/Emails/components/tables/SendersTable.tsx b/public/pages/Emails/components/tables/SendersTable.tsx index 52935e08..72b6355a 100644 --- a/public/pages/Emails/components/tables/SendersTable.tsx +++ b/public/pages/Emails/components/tables/SendersTable.tsx @@ -5,10 +5,9 @@ import { EuiBasicTable, - EuiButton, + EuiSmallButton, EuiContextMenuItem, EuiEmptyPrompt, - EuiFieldSearch, EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, @@ -244,9 +243,9 @@ export class SendersTable extends Component< actions={[ { component: ( - + Create SMTP sender - + ), }, ]} @@ -269,14 +268,14 @@ export class SendersTable extends Component< Actions - + } isOpen={this.state.isPopoverOpen} closePopover={() => this.setState({ isPopoverOpen: false })} @@ -319,9 +318,9 @@ export class SendersTable extends Component< title={

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 - + } /> } @@ -340,7 +339,7 @@ export class SendersTable extends Component< component: ( {({ onShow }) => ( - @@ -351,14 +350,14 @@ export class SendersTable extends Component< } > Delete - + )} ), }, { component: ( - @@ -368,14 +367,14 @@ export class SendersTable extends Component< } > Edit - + ), }, { component: ( - + Create SMTP sender - + ), }, ]} @@ -404,9 +403,9 @@ export class SendersTable extends Component< title={

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 - + } /> } From d42dbfd57ccfc1adba3b839c358039065d3140ce Mon Sep 17 00:00:00 2001 From: Riya Saxena Date: Mon, 19 Aug 2024 10:03:13 -0700 Subject: [PATCH 14/21] resolve merge conflict from main Signed-off-by: Riya Saxena --- .../Emails/components/tables/RecipientGroupsTable.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/public/pages/Emails/components/tables/RecipientGroupsTable.tsx b/public/pages/Emails/components/tables/RecipientGroupsTable.tsx index 514d9c85..b27b353a 100644 --- a/public/pages/Emails/components/tables/RecipientGroupsTable.tsx +++ b/public/pages/Emails/components/tables/RecipientGroupsTable.tsx @@ -6,16 +6,11 @@ import { EuiBasicTable, EuiSmallButton, -<<<<<<< HEAD EuiContextMenuItem, EuiEmptyPrompt, EuiCompressedFieldSearch, EuiFlexGroup, EuiFlexItem, -======= - EuiEmptyPrompt, - EuiCompressedFieldSearch, ->>>>>>> main EuiHorizontalRule, EuiLink, EuiPopover, @@ -28,7 +23,7 @@ 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 } from 'react'; -import { ApplicationStart, CoreStart } from '../../../../../../../src/core/public'; +import { CoreStart } from '../../../../../../../src/core/public'; import { RecipientGroupItemType, TableState, From 10bbaadce23c3685f305f5e396a605bc5b49abb6 Mon Sep 17 00:00:00 2001 From: Riya Saxena Date: Mon, 19 Aug 2024 10:10:18 -0700 Subject: [PATCH 15/21] resolve merge conflict from main Signed-off-by: Riya Saxena --- public/pages/Channels/components/details/ChannelDetails.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/pages/Channels/components/details/ChannelDetails.tsx b/public/pages/Channels/components/details/ChannelDetails.tsx index 31c33a14..11e86272 100644 --- a/public/pages/Channels/components/details/ChannelDetails.tsx +++ b/public/pages/Channels/components/details/ChannelDetails.tsx @@ -207,14 +207,14 @@ export function ChannelDetails(props: ChannelDetailsProps) { { renderComponent: (
- Send test message - +
), }, From dc8273b58cf3566bc865a4c4f8afecc08295190e Mon Sep 17 00:00:00 2001 From: Riya Saxena Date: Mon, 19 Aug 2024 13:31:34 -0700 Subject: [PATCH 16/21] refactored the code Signed-off-by: Riya Saxena --- public/pages/Channels/Channels.tsx | 115 +++++----------- .../components/details/ChannelDetails.tsx | 129 +++++++----------- public/pages/Emails/CreateSESSender.tsx | 14 +- public/pages/Emails/EmailGroups.tsx | 4 +- .../tables/RecipientGroupsTable.tsx | 93 +++++-------- .../components/tables/SESSendersTable.tsx | 96 +++++-------- .../Emails/components/tables/SendersTable.tsx | 92 +++++-------- 7 files changed, 192 insertions(+), 351 deletions(-) diff --git a/public/pages/Channels/Channels.tsx b/public/pages/Channels/Channels.tsx index 376558b6..17045efc 100644 --- a/public/pages/Channels/Channels.tsx +++ b/public/pages/Channels/Channels.tsx @@ -238,6 +238,36 @@ export class Channels extends MDSEnabledComponent ) + 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() ? ( @@ -261,50 +291,14 @@ export class Channels extends MDSEnabledComponent >
- + {channelControlsComponent}
- - this.setState({ selectedItems }) - } - items={this.state.items} - setItems={(items: ChannelItemType[]) => - this.setState({ items }) - } - refresh={this.refresh} - /> + {channelActionsComponent}
- 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} @@ -315,17 +309,7 @@ export class Channels extends MDSEnabledComponent actions={[ { component: ( - - this.setState({ selectedItems }) - } - items={this.state.items} - setItems={(items: ChannelItemType[]) => - this.setState({ items }) - } - refresh={this.refresh} - /> + channelActionsComponent ), }, { @@ -343,36 +327,9 @@ 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/components/details/ChannelDetails.tsx b/public/pages/Channels/components/details/ChannelDetails.tsx index 11e86272..c55f780b 100644 --- a/public/pages/Channels/components/details/ChannelDetails.tsx +++ b/public/pages/Channels/components/details/ChannelDetails.tsx @@ -34,10 +34,12 @@ import { ChannelDetailsActions } from './ChannelDetailsActions'; import { ChannelSettingsDetails } from './ChannelSettingsDetails'; import { getUseUpdatedUx } from '../../../../services/utils/constants'; import PageHeader from "../../../../components/PageHeader/PageHeader"; +import { CoreStart } from 'opensearch-dashboards/public'; +import { BrowserServices } from 'plugins/dashboards-notifications/public/models/interfaces'; interface ChannelDetailsProps extends RouteComponentProps<{ id: string -}> {} +}> { } const ChannelHeaderContent = ({ channel, setChannel }) => { const coreContext = useContext(CoreServicesContext)!; @@ -53,43 +55,47 @@ const ChannelHeaderContent = ({ channel, setChannel }) => { /> )}
- - {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'} - - )} - - )} - + {getMuteChannelComponent(channel, setChannel, servicesContext, coreContext)}
); }; +function getMuteChannelComponent(channel: any, setChannel: any, servicesContext: BrowserServices, coreContext: CoreStart) { + return + {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'} + + )} + + )} + ; +} + export function ChannelDetails(props: ChannelDetailsProps) { const coreContext = useContext(CoreServicesContext)!; const servicesContext = useContext(ServicesContext)!; @@ -220,16 +226,18 @@ export function ChannelDetails(props: ChannelDetailsProps) { }, ]; + const badgeComponent = + {channel?.is_enabled === undefined ? null : channel.is_enabled ? ( + Active + ) : ( + Muted + )} + ; + const badgeControls = [ { renderComponent: ( - - {channel?.is_enabled === undefined ? null : channel.is_enabled ? ( - Active - ) : ( - Muted - )} - + badgeComponent ), }, ]; @@ -246,13 +254,7 @@ export function ChannelDetails(props: ChannelDetailsProps) {

{channel?.name || '-'}

- - {channel?.is_enabled === undefined ? null : channel.is_enabled ? ( - Active - ) : ( - Muted - )} - + {badgeComponent} {channel && ( @@ -261,38 +263,7 @@ export function ChannelDetails(props: ChannelDetailsProps) { /> )} - - {channel && ( - - {({ onShow }) => ( - { - if (channel.is_enabled) { - onShow(MuteChannelModal, { - selected: [channel], - setSelected: (selected: React.SetStateAction[]) => 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'} - - )} - - )} - + {getMuteChannelComponent(channel, setChannel, servicesContext, coreContext)}
)} diff --git a/public/pages/Emails/CreateSESSender.tsx b/public/pages/Emails/CreateSESSender.tsx index 82f1cc98..63a6c960 100644 --- a/public/pages/Emails/CreateSESSender.tsx +++ b/public/pages/Emails/CreateSESSender.tsx @@ -155,15 +155,14 @@ export function CreateSESSender(props: CreateSESSenderProps) { ); const request = props.edit ? servicesContext.notificationService.updateConfig( - props.match.params.id!, - config - ) + props.match.params.id!, + config + ) : servicesContext.notificationService.createConfig(config); await request .then((response) => { coreContext.notifications.toasts.addSuccess( - `Sender ${senderName} successfully ${ - props.edit ? 'updated' : 'created' + `Sender ${senderName} successfully ${props.edit ? 'updated' : 'created' }.` ); setTimeout( @@ -174,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/EmailGroups.tsx b/public/pages/Emails/EmailGroups.tsx index 3770f2df..90a5cd2c 100644 --- a/public/pages/Emails/EmailGroups.tsx +++ b/public/pages/Emails/EmailGroups.tsx @@ -35,8 +35,8 @@ export function EmailGroups(props: EmailGroupsProps) { )} - ); diff --git a/public/pages/Emails/components/tables/RecipientGroupsTable.tsx b/public/pages/Emails/components/tables/RecipientGroupsTable.tsx index b27b353a..242a4b55 100644 --- a/public/pages/Emails/components/tables/RecipientGroupsTable.tsx +++ b/public/pages/Emails/components/tables/RecipientGroupsTable.tsx @@ -265,10 +265,36 @@ export class RecipientGroupsTable extends Component< const totalEmailGroups = ( -

({this.state.total})

-
+

({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 ( <> {getUseUpdatedUx() ? ( @@ -293,12 +319,7 @@ export class RecipientGroupsTable extends Component< > - + {searchComponent} - - 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} - /> + {tableComponent} ) : ( @@ -408,9 +408,7 @@ export class RecipientGroupsTable extends Component< }, { component: ( - - Create recipient group - + createRecepientButton ), }, ]} @@ -421,35 +419,10 @@ export class RecipientGroupsTable extends Component< titleSize="m" total={this.state.total} > - + {searchComponent} - 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} - /> + {tableComponent} )} diff --git a/public/pages/Emails/components/tables/SESSendersTable.tsx b/public/pages/Emails/components/tables/SESSendersTable.tsx index 2571ff5e..aed3287b 100644 --- a/public/pages/Emails/components/tables/SESSendersTable.tsx +++ b/public/pages/Emails/components/tables/SESSendersTable.tsx @@ -212,6 +212,34 @@ export class SESSendersTable extends Component< }, ]; + 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 ( <> {getUseUpdatedUx() ? ( @@ -236,12 +264,7 @@ export class SESSendersTable extends Component< > - + {searchComponent} - - 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" - /> + {tableComponent} ) : ( - Create SES sender - + createSESButton ), }, ]} @@ -365,37 +363,9 @@ export class SESSendersTable extends Component< titleSize="m" total={this.state.total} > - + {searchComponent} - - 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" - /> + {tableComponent} )} diff --git a/public/pages/Emails/components/tables/SendersTable.tsx b/public/pages/Emails/components/tables/SendersTable.tsx index 72b6355a..efee0179 100644 --- a/public/pages/Emails/components/tables/SendersTable.tsx +++ b/public/pages/Emails/components/tables/SendersTable.tsx @@ -20,7 +20,7 @@ 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 } from 'react'; -import { CoreStart } from '../../../../../../../src/core/public'; +import { CoreStart } from '../../../../../../../src/core/public'; import { SenderItemType, TableState } from '../../../../../models/interfaces'; import { ContentPanel, @@ -234,6 +234,32 @@ export class SendersTable extends Component< }, ]; + 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 ( <> {getUseUpdatedUx() ? ( @@ -258,11 +284,7 @@ export class SendersTable extends Component< > - this.setState({ filters })} - /> + {senderControlComponent} - - 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} - /> + {basicTableComponent} ) : ( - Create SMTP sender - + createSMTPButton ), }, ]} @@ -385,35 +383,9 @@ export class SendersTable extends Component< titleSize="m" total={this.state.total} > - this.setState({ filters })} - /> + {senderControlComponent} - - 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} - /> + {basicTableComponent} )} From 9215075dd4ad6018d9f92384d65996efbe8f4822 Mon Sep 17 00:00:00 2001 From: Riya Saxena Date: Mon, 19 Aug 2024 15:18:12 -0700 Subject: [PATCH 17/21] refactored the code Signed-off-by: Riya Saxena --- .../ChannelDetails.test.tsx.snap | 99 ++++++++++---- .../components/details/ChannelDetails.tsx | 129 ++++++++---------- 2 files changed, 127 insertions(+), 101 deletions(-) diff --git a/public/pages/Channels/__tests__/__snapshots__/ChannelDetails.test.tsx.snap b/public/pages/Channels/__tests__/__snapshots__/ChannelDetails.test.tsx.snap index daf045d7..f62687c9 100644 --- a/public/pages/Channels/__tests__/__snapshots__/ChannelDetails.test.tsx.snap +++ b/public/pages/Channels/__tests__/__snapshots__/ChannelDetails.test.tsx.snap @@ -2,19 +2,34 @@ exports[` spec handles a non-existing channel 1`] = `
-
-

+
- - -

+
+
+

+ - +

+
+
+
+
-
@@ -157,19 +172,34 @@ exports[` spec handles a non-existing channel 1`] = ` exports[` spec renders a specific channel 1`] = `
-
-

+
- - -

+
+
+

+ - +

+
+
+
+
-
@@ -311,19 +341,34 @@ exports[` spec renders a specific channel 1`] = ` `; exports[` spec renders the component 1`] = ` -
-

+
- - -

+
+
+

+ - +

+
+
+
+
-
diff --git a/public/pages/Channels/components/details/ChannelDetails.tsx b/public/pages/Channels/components/details/ChannelDetails.tsx index c55f780b..1b8f5daf 100644 --- a/public/pages/Channels/components/details/ChannelDetails.tsx +++ b/public/pages/Channels/components/details/ChannelDetails.tsx @@ -34,68 +34,11 @@ import { ChannelDetailsActions } from './ChannelDetailsActions'; import { ChannelSettingsDetails } from './ChannelSettingsDetails'; import { getUseUpdatedUx } from '../../../../services/utils/constants'; import PageHeader from "../../../../components/PageHeader/PageHeader"; -import { CoreStart } from 'opensearch-dashboards/public'; -import { BrowserServices } from 'plugins/dashboards-notifications/public/models/interfaces'; interface ChannelDetailsProps extends RouteComponentProps<{ id: string }> { } -const ChannelHeaderContent = ({ channel, setChannel }) => { - const coreContext = useContext(CoreServicesContext)!; - const servicesContext = useContext(ServicesContext)!; - - return ( - - - - {channel && ( - - )} - - {getMuteChannelComponent(channel, setChannel, servicesContext, coreContext)} - - ); -}; - - -function getMuteChannelComponent(channel: any, setChannel: any, servicesContext: BrowserServices, coreContext: CoreStart) { - return - {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'} - - )} - - )} - ; -} - export function ChannelDetails(props: ChannelDetailsProps) { const coreContext = useContext(CoreServicesContext)!; const servicesContext = useContext(ServicesContext)!; @@ -201,13 +144,51 @@ 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 ), }, { @@ -249,23 +230,23 @@ export function ChannelDetails(props: ChannelDetailsProps) { appBadgeControls={getUseUpdatedUx() ? badgeControls : []} > {!getUseUpdatedUx() && ( -
- -

{channel?.name || '-'}

-
+ + - {badgeComponent} - - {channel && ( - - )} + +

{channel?.name ?? '-'}

+
- {getMuteChannelComponent(channel, setChannel, servicesContext, coreContext)} + {badgeComponent}
-
+ + {actionsAndMuteComponent} + )} From 7f16773f85db8ac7cca512e93178fb6f46a713db Mon Sep 17 00:00:00 2001 From: Riya Saxena Date: Mon, 19 Aug 2024 17:49:18 -0700 Subject: [PATCH 18/21] addressed the comment Signed-off-by: Riya Saxena --- public/pages/Channels/Channels.tsx | 91 +++++++++++++----------------- 1 file changed, 38 insertions(+), 53 deletions(-) diff --git a/public/pages/Channels/Channels.tsx b/public/pages/Channels/Channels.tsx index 17045efc..7d3bea3b 100644 --- a/public/pages/Channels/Channels.tsx +++ b/public/pages/Channels/Channels.tsx @@ -270,25 +270,13 @@ export class Channels extends MDSEnabledComponent return ( <> - {getUseUpdatedUx() ? ( - - ), - } - ]} - /> - } - > + {getUseUpdatedUx() ? ( + <> + +
{channelControlsComponent} @@ -300,40 +288,37 @@ export class Channels extends MDSEnabledComponent {basicTableComponent} - - - ) : ( - - Create channel - - ), - }, - ]} - /> - } - bodyStyles={{ padding: 'initial' }} - title="Channels" - titleSize="m" - total={this.state.total} - > - {channelControlsComponent} - - {basicTableComponent} - - - )} - + + ) : ( + + Create channel + + ), + }, + ]} + /> + } + bodyStyles={{ padding: 'initial' }} + title="Channels" + titleSize="m" + total={this.state.total} + > + {channelControlsComponent} + + {basicTableComponent} + + )} + + ); } }; From 03f13df4328903b29334cc821dc766b2b7425199 Mon Sep 17 00:00:00 2001 From: Riya Saxena Date: Mon, 19 Aug 2024 22:30:13 -0700 Subject: [PATCH 19/21] addressed the comment Signed-off-by: Riya Saxena --- .../components/details/ChannelDetails.tsx | 2 +- .../tables/RecipientGroupsTable.tsx | 129 ++-- test/jest.config.js | 2 +- test/polyfills.ts | 9 - test/polyfills/mutationObserver.js | 572 ------------------ 5 files changed, 59 insertions(+), 655 deletions(-) delete mode 100644 test/polyfills.ts delete mode 100644 test/polyfills/mutationObserver.js diff --git a/public/pages/Channels/components/details/ChannelDetails.tsx b/public/pages/Channels/components/details/ChannelDetails.tsx index 1b8f5daf..3cefd1c0 100644 --- a/public/pages/Channels/components/details/ChannelDetails.tsx +++ b/public/pages/Channels/components/details/ChannelDetails.tsx @@ -229,7 +229,7 @@ export function ChannelDetails(props: ChannelDetailsProps) { appRightControls={getUseUpdatedUx() ? rightControls : []} appBadgeControls={getUseUpdatedUx() ? badgeControls : []} > - {!getUseUpdatedUx() && ( + {( {getUseUpdatedUx() ? ( - - - ), - } - ]} - /> - } - > - - - {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} - - + <> + + + + + {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} + + ) : ( @@ -421,11 +406,11 @@ export class RecipientGroupsTable extends Component< > {searchComponent} - {tableComponent} )} + ); } }; diff --git a/test/jest.config.js b/test/jest.config.js index b1121f39..a88c0ff5 100644 --- a/test/jest.config.js +++ b/test/jest.config.js @@ -6,7 +6,7 @@ module.exports = { rootDir: '../', setupFiles: ['/test/setupTests.ts'], - setupFilesAfterEnv: ['/test/setup.jest.ts', '/test/polyfills.ts'], + setupFilesAfterEnv: ['/test/setup.jest.ts'], roots: [''], testMatch: ['**/*.test.js', '**/*.test.jsx', '**/*.test.ts', '**/*.test.tsx'], clearMocks: true, diff --git a/test/polyfills.ts b/test/polyfills.ts deleted file mode 100644 index e39895c7..00000000 --- a/test/polyfills.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -// @ts-ignore -import { MutationObserver } from './polyfills/mutationObserver'; - -Object.defineProperty(window, 'MutationObserver', { value: MutationObserver }); \ No newline at end of file diff --git a/test/polyfills/mutationObserver.js b/test/polyfills/mutationObserver.js deleted file mode 100644 index 53709e32..00000000 --- a/test/polyfills/mutationObserver.js +++ /dev/null @@ -1,572 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -/* eslint-disable */ -// transpiled typescript->javascript from -// https://github.com/aurelia/pal-nodejs/blob/master/src/polyfills/mutation-observer.ts - -/* - * Based on Shim for MutationObserver interface - * Author: Graeme Yeates (github.com/megawac) - * Repository: https://github.com/megawac/MutationObserver.js - */ -import { EventEmitter } from 'events'; - -var __extends = - (this && this.__extends) || - (function () { - var extendStatics = - Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && - function (d, b) { - d.__proto__ = b; - }) || - function (d, b) { - for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; - }; - return function (d, b) { - extendStatics(d, b); - function __() { - this.constructor = d; - } - d.prototype = b === null ? Object.create(b) : ((__.prototype = b.prototype), new __()); - }; - })(); - -module.exports = {}; - -Object.defineProperty(module.exports, '__esModule', { value: true }); -var Util = /** @class */ (function () { - function Util() {} - Util.clone = function ($target, config) { - var recurse = true; // set true so childList we'll always check the first level - return (function copy($target) { - var elestruct = { - /** @type {Node} */ - node: $target, - charData: null, - attr: null, - kids: null, - }; - // Store current character data of target text or comment node if the config requests - // those properties to be observed. - if (config.charData && ($target.nodeType === 3 || $target.nodeType === 8)) { - elestruct.charData = $target.nodeValue; - } else { - // Add attr only if subtree is specified or top level and avoid if - // attributes is a document object (#13). - if (config.attr && recurse && $target.nodeType === 1) { - /** - * clone live attribute list to an object structure {name: val} - * @type {Object.} - */ - elestruct.attr = Util.reduce( - $target.attributes, - function (memo, attr) { - if (!config.afilter || config.afilter[attr.name]) { - memo[attr.name] = attr.value; - } - return memo; - }, - {} - ); - } - // whether we should iterate the children of $target node - if (recurse && (config.kids || config.charData || (config.attr && config.descendents))) { - /** @type {Array.} : Array of custom clone */ - elestruct.kids = Util.map($target.childNodes, copy); - } - recurse = config.descendents; - } - return elestruct; - })($target); - }; - /** - * indexOf an element in a collection of custom nodes - * - * @param {NodeList} set - * @param {!Object} $node : A custom cloned nodeg333 - * @param {number} idx : index to start the loop - * @return {number} - */ - Util.indexOfCustomNode = function (set, $node, idx) { - var JSCompiler_renameProperty = function (a) { - return a; - }; - return this.indexOf(set, $node, idx, JSCompiler_renameProperty('node')); - }; - /** - * Attempt to uniquely id an element for hashing. We could optimize this for legacy browsers but it hopefully wont be called enough to be a concern - * - * @param {Node} $ele - * @return {(string|number)} - */ - Util.getElementId = function ($ele) { - try { - return $ele.id || ($ele[this.expando] = $ele[this.expando] || this.counter++); - } catch (e) { - // ie <8 will throw if you set an unknown property on a text node - try { - return $ele.nodeValue; // naive - } catch (shitie) { - // when text node is removed: https://gist.github.com/megawac/8355978 :( - return this.counter++; - } - } - }; - /** - * **map** Apply a mapping function to each item of a set - * @param {Array|NodeList} set - * @param {Function} iterator - */ - Util.map = function (set, iterator) { - var results = []; - for (var index = 0; index < set.length; index++) { - results[index] = iterator(set[index], index, set); - } - return results; - }; - /** - * **Reduce** builds up a single result from a list of values - * @param {Array|NodeList|NamedNodeMap} set - * @param {Function} iterator - * @param {*} [memo] Initial value of the memo. - */ - Util.reduce = function (set, iterator, memo) { - for (var index = 0; index < set.length; index++) { - memo = iterator(memo, set[index], index, set); - } - return memo; - }; - /** - * **indexOf** find index of item in collection. - * @param {Array|NodeList} set - * @param {Object} item - * @param {number} idx - * @param {string} [prop] Property on set item to compare to item - */ - Util.indexOf = function (set, item, idx, prop) { - for (; /*idx = ~~idx*/ idx < set.length; idx++) { - // start idx is always given as this is internal - if ((prop ? set[idx][prop] : set[idx]) === item) return idx; - } - return -1; - }; - /** - * @param {Object} obj - * @param {(string|number)} prop - * @return {boolean} - */ - Util.has = function (obj, prop) { - return obj[prop] !== undefined; // will be nicely inlined by gcc - }; - Util.counter = 1; - Util.expando = 'mo_id'; - return Util; -})(); -module.exports.Util = Util; -var MutationObserver = /** @class */ (function () { - function MutationObserver(listener) { - var _this = this; - this._watched = []; - this._listener = null; - this._period = 30; - this._timeout = null; - this._disposed = false; - this._notifyListener = null; - this._watched = []; - this._listener = listener; - this._period = 30; - this._notifyListener = function () { - _this.scheduleMutationCheck(_this); - }; - } - MutationObserver.prototype.observe = function ($target, config) { - var settings = { - attr: !!(config.attributes || config.attributeFilter || config.attributeOldValue), - // some browsers enforce that subtree must be set with childList, attributes or characterData. - // We don't care as spec doesn't specify this rule. - kids: !!config.childList, - descendents: !!config.subtree, - charData: !!(config.characterData || config.characterDataOldValue), - afilter: null, - }; - MutationNotifier.getInstance().on('changed', this._notifyListener); - var watched = this._watched; - // remove already observed target element from pool - for (var i = 0; i < watched.length; i++) { - if (watched[i].tar === $target) watched.splice(i, 1); - } - if (config.attributeFilter) { - /** - * converts to a {key: true} dict for faster lookup - * @type {Object.} - */ - settings.afilter = Util.reduce( - config.attributeFilter, - function (a, b) { - a[b] = true; - return a; - }, - {} - ); - } - watched.push({ - tar: $target, - fn: this.createMutationSearcher($target, settings), - }); - }; - MutationObserver.prototype.takeRecords = function () { - var mutations = []; - var watched = this._watched; - for (var i = 0; i < watched.length; i++) { - watched[i].fn(mutations); - } - return mutations; - }; - MutationObserver.prototype.disconnect = function () { - this._watched = []; // clear the stuff being observed - MutationNotifier.getInstance().removeListener('changed', this._notifyListener); - this._disposed = true; - clearTimeout(this._timeout); // ready for garbage collection - this._timeout = null; - }; - MutationObserver.prototype.createMutationSearcher = function ($target, config) { - var _this = this; - /** type {Elestuct} */ - var $oldstate = Util.clone($target, config); // create the cloned datastructure - /** - * consumes array of mutations we can push to - * - * @param {Array.} mutations - */ - return function (mutations) { - var olen = mutations.length; - var dirty; - if (config.charData && $target.nodeType === 3 && $target.nodeValue !== $oldstate.charData) { - mutations.push( - new MutationRecord({ - type: 'characterData', - target: $target, - oldValue: $oldstate.charData, - }) - ); - } - // Alright we check base level changes in attributes... easy - if (config.attr && $oldstate.attr) { - _this.findAttributeMutations(mutations, $target, $oldstate.attr, config.afilter); - } - // check childlist or subtree for mutations - if (config.kids || config.descendents) { - dirty = _this.searchSubtree(mutations, $target, $oldstate, config); - } - // reclone data structure if theres changes - if (dirty || mutations.length !== olen) { - /** type {Elestuct} */ - $oldstate = Util.clone($target, config); - } - }; - }; - MutationObserver.prototype.scheduleMutationCheck = function (observer) { - var _this = this; - // Only schedule if there isn't already a timer. - if (!observer._timeout) { - observer._timeout = setTimeout(function () { - return _this.mutationChecker(observer); - }, this._period); - } - }; - MutationObserver.prototype.mutationChecker = function (observer) { - // allow scheduling a new timer. - observer._timeout = null; - var mutations = observer.takeRecords(); - if (mutations.length) { - // fire away - // calling the listener with context is not spec but currently consistent with FF and WebKit - observer._listener(mutations, observer); - } - }; - MutationObserver.prototype.searchSubtree = function (mutations, $target, $oldstate, config) { - var _this = this; - // Track if the tree is dirty and has to be recomputed (#14). - var dirty; - /* - * Helper to identify node rearrangment and stuff... - * There is no gaurentee that the same node will be identified for both added and removed nodes - * if the positions have been shuffled. - * conflicts array will be emptied by end of operation - */ - var _resolveConflicts = function (conflicts, node, $kids, $oldkids, numAddedNodes) { - // the distance between the first conflicting node and the last - var distance = conflicts.length - 1; - // prevents same conflict being resolved twice consider when two nodes switch places. - // only one should be given a mutation event (note -~ is used as a math.ceil shorthand) - var counter = -~((distance - numAddedNodes) / 2); - var $cur; - var oldstruct; - var conflict; - while ((conflict = conflicts.pop())) { - $cur = $kids[conflict.i]; - oldstruct = $oldkids[conflict.j]; - // attempt to determine if there was node rearrangement... won't gaurentee all matches - // also handles case where added/removed nodes cause nodes to be identified as conflicts - if (config.kids && counter && Math.abs(conflict.i - conflict.j) >= distance) { - mutations.push( - new MutationRecord({ - type: 'childList', - target: node, - addedNodes: [$cur], - removedNodes: [$cur], - // haha don't rely on this please - nextSibling: $cur.nextSibling, - previousSibling: $cur.previousSibling, - }) - ); - counter--; // found conflict - } - // Alright we found the resorted nodes now check for other types of mutations - if (config.attr && oldstruct.attr) - _this.findAttributeMutations(mutations, $cur, oldstruct.attr, config.afilter); - if (config.charData && $cur.nodeType === 3 && $cur.nodeValue !== oldstruct.charData) { - mutations.push( - new MutationRecord({ - type: 'characterData', - target: $cur, - oldValue: oldstruct.charData, - }) - ); - } - // now look @ subtree - if (config.descendents) _findMutations($cur, oldstruct); - } - }; - /** - * Main worker. Finds and adds mutations if there are any - * @param {Node} node - * @param {!Object} old : A cloned data structure using internal clone - */ - var _findMutations = function (node, old) { - var $kids = node.childNodes; - var $oldkids = old.kids; - var klen = $kids.length; - // $oldkids will be undefined for text and comment nodes - var olen = $oldkids ? $oldkids.length : 0; - // if (!olen && !klen) return; // both empty; clearly no changes - // we delay the intialization of these for marginal performance in the expected case (actually quite signficant on large subtrees when these would be otherwise unused) - // map of checked element of ids to prevent registering the same conflict twice - var map; - // array of potential conflicts (ie nodes that may have been re arranged) - var conflicts; - var id; // element id from getElementId helper - var idx; // index of a moved or inserted element - var oldstruct; - // current and old nodes - var $cur; - var $old; - // track the number of added nodes so we can resolve conflicts more accurately - var numAddedNodes = 0; - // iterate over both old and current child nodes at the same time - var i = 0; - var j = 0; - // while there is still anything left in $kids or $oldkids (same as i < $kids.length || j < $oldkids.length;) - while (i < klen || j < olen) { - // current and old nodes at the indexs - $cur = $kids[i]; - oldstruct = $oldkids[j]; - $old = oldstruct && oldstruct.node; - if ($cur === $old) { - // expected case - optimized for this case - // check attributes as specified by config - if (config.attr && oldstruct.attr) { - /* oldstruct.attr instead of textnode check */ - _this.findAttributeMutations(mutations, $cur, oldstruct.attr, config.afilter); - } - // check character data if node is a comment or textNode and it's being observed - if ( - config.charData && - oldstruct.charData !== undefined && - $cur.nodeValue !== oldstruct.charData - ) { - mutations.push( - new MutationRecord({ - type: 'characterData', - target: $cur, - }) - ); - } - // resolve conflicts; it will be undefined if there are no conflicts - otherwise an array - if (conflicts) _resolveConflicts(conflicts, node, $kids, $oldkids, numAddedNodes); - // recurse on next level of children. Avoids the recursive call when there are no children left to iterate - if ( - config.descendents && - ($cur.childNodes.length || (oldstruct.kids && oldstruct.kids.length)) - ) - _findMutations($cur, oldstruct); - i++; - j++; - } else { - // (uncommon case) lookahead until they are the same again or the end of children - dirty = true; - if (!map) { - // delayed initalization (big perf benefit) - map = {}; - conflicts = []; - } - if ($cur) { - // check id is in the location map otherwise do a indexOf search - if (!map[(id = Util.getElementId($cur))]) { - // to prevent double checking - // mark id as found - map[id] = true; - // custom indexOf using comparitor checking oldkids[i].node === $cur - if ((idx = Util.indexOfCustomNode($oldkids, $cur, j)) === -1) { - if (config.kids) { - mutations.push( - new MutationRecord({ - type: 'childList', - target: node, - addedNodes: [$cur], - nextSibling: $cur.nextSibling, - previousSibling: $cur.previousSibling, - }) - ); - numAddedNodes++; - } - } else { - conflicts.push({ - i: i, - j: idx, - }); - } - } - i++; - } - if ( - $old && - // special case: the changes may have been resolved: i and j appear congurent so we can continue using the expected case - $old !== $kids[i] - ) { - if (!map[(id = Util.getElementId($old))]) { - map[id] = true; - if ((idx = Util.indexOf($kids, $old, i)) === -1) { - if (config.kids) { - mutations.push( - new MutationRecord({ - type: 'childList', - target: old.node, - removedNodes: [$old], - nextSibling: $oldkids[j + 1], - previousSibling: $oldkids[j - 1], - }) - ); - numAddedNodes--; - } - } else { - conflicts.push({ - i: idx, - j: j, - }); - } - } - j++; - } - } // end uncommon case - } // end loop - // resolve any remaining conflicts - if (conflicts) _resolveConflicts(conflicts, node, $kids, $oldkids, numAddedNodes); - }; - _findMutations($target, $oldstate); - return dirty; - }; - MutationObserver.prototype.findAttributeMutations = function ( - mutations, - $target, - $oldstate, - filter - ) { - var checked = {}; - var attributes = $target.attributes; - var attr; - var name; - var i = attributes.length; - while (i--) { - attr = attributes[i]; - name = attr.name; - if (!filter || Util.has(filter, name)) { - if (attr.value !== $oldstate[name]) { - // The pushing is redundant but gzips very nicely - mutations.push( - new MutationRecord({ - type: 'attributes', - target: $target, - attributeName: name, - oldValue: $oldstate[name], - attributeNamespace: attr.namespaceURI, // in ie<8 it incorrectly will return undefined - }) - ); - } - checked[name] = true; - } - } - for (name in $oldstate) { - if (!checked[name]) { - mutations.push( - new MutationRecord({ - target: $target, - type: 'attributes', - attributeName: name, - oldValue: $oldstate[name], - }) - ); - } - } - }; - return MutationObserver; -})(); -module.exports.MutationObserver = MutationObserver; -var MutationRecord = /** @class */ (function () { - function MutationRecord(data) { - var settings = { - type: null, - target: null, - addedNodes: [], - removedNodes: [], - previousSibling: null, - nextSibling: null, - attributeName: null, - attributeNamespace: null, - oldValue: null, - }; - for (var prop in data) { - if (Util.has(settings, prop) && data[prop] !== undefined) settings[prop] = data[prop]; - } - return settings; - } - return MutationRecord; -})(); -module.exports.MutationRecord = MutationRecord; -var MutationNotifier = /** @class */ (function (_super) { - __extends(MutationNotifier, _super); - function MutationNotifier() { - var _this = _super.call(this) || this; - _this.setMaxListeners(100); - return _this; - } - MutationNotifier.getInstance = function () { - if (!MutationNotifier._instance) { - MutationNotifier._instance = new MutationNotifier(); - } - return MutationNotifier._instance; - }; - MutationNotifier.prototype.destruct = function () { - this.removeAllListeners('changed'); - }; - MutationNotifier.prototype.notifyChanged = function (node) { - this.emit('changed', node); - }; - MutationNotifier._instance = null; - return MutationNotifier; -})(EventEmitter); -module.exports.MutationNotifier = MutationNotifier; \ No newline at end of file From 37c3d37bd242f9b3c218a3af679041078e0a556a Mon Sep 17 00:00:00 2001 From: Riya Saxena Date: Mon, 19 Aug 2024 22:35:33 -0700 Subject: [PATCH 20/21] addressed the comment Signed-off-by: Riya Saxena --- public/pages/Channels/components/details/ChannelDetails.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/pages/Channels/components/details/ChannelDetails.tsx b/public/pages/Channels/components/details/ChannelDetails.tsx index 3cefd1c0..cef7c581 100644 --- a/public/pages/Channels/components/details/ChannelDetails.tsx +++ b/public/pages/Channels/components/details/ChannelDetails.tsx @@ -226,8 +226,8 @@ export function ChannelDetails(props: ChannelDetailsProps) { return ( <> {( Date: Mon, 19 Aug 2024 22:58:52 -0700 Subject: [PATCH 21/21] addressed the comment Signed-off-by: Riya Saxena --- package.json | 4 ---- .../pages/Channels/components/details/ChannelDetails.tsx | 1 - test/setup.jest.ts | 9 --------- 3 files changed, 14 deletions(-) diff --git a/package.json b/package.json index 2e0edd9b..8cdba346 100644 --- a/package.json +++ b/package.json @@ -20,10 +20,6 @@ "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": { - "tough-cookie": "^4.1.3", - "web-streams-polyfill": "^4.0.0" - }, "devDependencies": { "@types/enzyme-adapter-react-16": "^1.0.6", "@types/showdown": "^1.9.3", diff --git a/public/pages/Channels/components/details/ChannelDetails.tsx b/public/pages/Channels/components/details/ChannelDetails.tsx index cef7c581..96b71f4f 100644 --- a/public/pages/Channels/components/details/ChannelDetails.tsx +++ b/public/pages/Channels/components/details/ChannelDetails.tsx @@ -32,7 +32,6 @@ import { MuteChannelModal } from '../modals/MuteChannelModal'; import { ChannelDetailItems } from './ChannelDetailItems'; import { ChannelDetailsActions } from './ChannelDetailsActions'; import { ChannelSettingsDetails } from './ChannelSettingsDetails'; -import { getUseUpdatedUx } from '../../../../services/utils/constants'; import PageHeader from "../../../../components/PageHeader/PageHeader"; interface ChannelDetailsProps extends RouteComponentProps<{ diff --git a/test/setup.jest.ts b/test/setup.jest.ts index 7aa54103..ad09db0d 100644 --- a/test/setup.jest.ts +++ b/test/setup.jest.ts @@ -2,15 +2,6 @@ * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ - -import '@testing-library/jest-dom/extend-expect'; -import { ReadableStream } from 'web-streams-polyfill'; // Polyfill for ReadableStream -import { TextDecoder, TextEncoder } from 'util'; -global.TextEncoder = TextEncoder; -// @ts-expect-error -global.TextDecoder = TextDecoder; -// @ts-expect-error -global.ReadableStream = ReadableStream; import { configure } from '@testing-library/react'; configure({ testIdAttribute: 'data-test-subj' });