From b47317dcd28ea66c310e930e6c9fb00f709c6166 Mon Sep 17 00:00:00 2001 From: Seyi Adebajo Date: Mon, 21 May 2018 16:31:20 -0700 Subject: [PATCH] 5674 adds ability to view truncated text in notification toast as a dialog --- .../app/components/dataset-compliance.ts | 12 ++- wherehows-web/app/services/notifications.ts | 87 +++++++++++++++---- .../notifications/_notification-toast.scss | 25 +++++- .../templates/components/confirm-dialog.hbs | 16 ++-- .../components/notifications-service.hbs | 12 ++- .../app/typings/app/dataset-compliance.d.ts | 2 +- .../app/utils/notifications/notifications.ts | 44 ++++++++++ 7 files changed, 160 insertions(+), 38 deletions(-) create mode 100644 wherehows-web/app/utils/notifications/notifications.ts diff --git a/wherehows-web/app/components/dataset-compliance.ts b/wherehows-web/app/components/dataset-compliance.ts index 1a0624680a..1b17448e49 100644 --- a/wherehows-web/app/components/dataset-compliance.ts +++ b/wherehows-web/app/components/dataset-compliance.ts @@ -66,6 +66,7 @@ import { emptyRegexSource } from 'wherehows-web/utils/validators/regexp'; import { NonIdLogicalType } from 'wherehows-web/constants/datasets/compliance'; import { pick } from 'lodash'; import { trackableEvent, TrackableEventCategory } from 'wherehows-web/constants/analytics/event-tracking'; +import { notificationDialogActionFactory } from 'wherehows-web/utils/notifications/notifications'; const { complianceDataException, @@ -860,7 +861,7 @@ export default class DatasetCompliance extends Component { * @return {Promise} */ showPurgeExemptionWarning(this: DatasetCompliance): Promise { - const dialogActions = {}; + const { dialogActions, dismissedOrConfirmed } = notificationDialogActionFactory(); get(this, 'notifications').notify(NotificationEvent.confirm, { header: 'Confirm purge exemption', @@ -869,10 +870,7 @@ export default class DatasetCompliance extends Component { dialogActions }); - return new Promise((resolve, reject): void => { - dialogActions['didConfirm'] = (): void => resolve(); - dialogActions['didDismiss'] = (): void => reject(); - }); + return dismissedOrConfirmed; } /** @@ -1127,9 +1125,9 @@ export default class DatasetCompliance extends Component { /** * Handles post processing tasks after the purge policy step has been completed - * @returns {(Promise)} + * @returns {(Promise)} */ - didEditPurgePolicy(this: DatasetCompliance): Promise { + didEditPurgePolicy(this: DatasetCompliance): Promise { const { complianceType = null } = get(this, 'complianceInfo') || {}; if (!complianceType) { diff --git a/wherehows-web/app/services/notifications.ts b/wherehows-web/app/services/notifications.ts index 2ef77dc7d9..fc0d6a57c9 100644 --- a/wherehows-web/app/services/notifications.ts +++ b/wherehows-web/app/services/notifications.ts @@ -2,6 +2,8 @@ import Service from '@ember/service'; import { setProperties, get, set } from '@ember/object'; import { delay } from 'wherehows-web/utils/promise-delay'; import { action } from '@ember-decorators/object'; +import { fleece } from 'wherehows-web/utils/object'; +import { notificationDialogActionFactory } from 'wherehows-web/utils/notifications/notifications'; /** * Flag indicating the current notification queue is being processed @@ -10,7 +12,7 @@ import { action } from '@ember-decorators/object'; let isBuffering = false; /** - * String literal of available notifications + * String enum of available notifications */ export enum NotificationEvent { success = 'success', @@ -20,9 +22,12 @@ export enum NotificationEvent { } /** - * String literal for notification types + * String enum of notification types */ -type NotificationType = 'modal' | 'toast'; +enum NotificationType { + Modal = 'modal', + Toast = 'toast' +} /** * Describes the proxy handler for INotifications @@ -37,10 +42,15 @@ interface INotificationHandlerTrap { * @interface IConfirmOptions */ export interface IConfirmOptions { + // Header text for the confirmation dialog header: string; + // Content to be displayed in the confirmation dialog content: string; - dismissButtonText?: string; - confirmButtonText?: string; + // Text for button to dismiss dialog action, if false, button will not be rendered + dismissButtonText?: string | false; + // Text for button to confirm dialog action, if false, button will not be rendered + confirmButtonText?: string | false; + // Action handlers for dialog button on dismissal or otherwise dialogActions: { didConfirm: () => any; didDismiss: () => any; @@ -64,7 +74,7 @@ interface IToast { interface INotification { // The properties for the notification props: IConfirmOptions | IToast; - // The type if the notification + // The type of the notification type: NotificationType; // Object holding the queue state for the notification notificationResolution: INotificationResolver; @@ -119,7 +129,7 @@ const makeToast = (props: IToast): INotification => { return { props, - type: 'toast', + type: NotificationType.Toast, notificationResolution }; }; @@ -131,18 +141,27 @@ const notificationHandlers: INotificationHandler = { * @return {INotification} */ confirm(props: IConfirmOptions): INotification { - let notificationResolution: INotificationResolver = { + const notificationResolution: INotificationResolver = { get queueAwaiter() { return createNotificationAwaiter(this); } }; - // Set default values for button text if none are provided by consumer props = { dismissButtonText: 'No', confirmButtonText: 'Yes', ...props }; + const { dismissButtonText, confirmButtonText } = props; + // Removes dismiss or confirm buttons if set to false + let resolvedProps: IConfirmOptions = + dismissButtonText === false + ? fleece(['dismissButtonText'])(props) + : props; + resolvedProps = + confirmButtonText === false + ? fleece(['confirmButtonText'])(props) + : props; return { - props, - type: 'modal', + props: resolvedProps, + type: NotificationType.Modal, notificationResolution }; }, @@ -223,7 +242,7 @@ const asyncDequeue = function(notificationsQueue: Array) { * @param {INotification} notification * @param {Array} notificationsQueue */ -const enqueue = (notification: INotification, notificationsQueue: Array) => { +const enqueue = (notification: INotification, notificationsQueue: Array): void => { notificationsQueue.unshift(notification); asyncDequeue(notificationsQueue); }; @@ -263,12 +282,14 @@ export default class Notifications extends Service { * @param {INotification} notification */ setCurrentNotification = async (notification: INotification) => { - if (notification.type === 'modal') { - setProperties(this, { modal: notification, isShowingModal: true }); + const { type, props } = notification; + + if (type === NotificationType.Modal) { + setProperties(this, { modal: notification, isShowingModal: true }); } else { - const { props } = notification; const toastDelay = delay((props).duration); - setProperties(this, { toast: notification, isShowingToast: true }); + + setProperties(this, { toast: notification, isShowingToast: true }); if (!(props).isSticky) { await toastDelay; @@ -297,21 +318,24 @@ export default class Notifications extends Service { /** * Removes the current toast from view and invokes the notification resolution resolver + * @memberof Notifications */ @action - dismissToast(this: Notifications) { + dismissToast() { const { notificationResolution: { onComplete } }: INotification = get(this, 'toast'); + set(this, 'isShowingToast', false); onComplete && onComplete(); } /** * Ignores the modal, invokes the user supplied didDismiss callback + * @memberof Notifications */ @action - dismissModal(this: Notifications) { + dismissModal() { const { props, notificationResolution: { onComplete } @@ -327,13 +351,15 @@ export default class Notifications extends Service { /** * Confirms the dialog and invokes the user supplied didConfirm callback + * @memberof Notifications */ @action - confirmModal(this: Notifications) { + confirmModal() { const { props, notificationResolution: { onComplete } }: INotification = get(this, 'modal'); + if ((props).dialogActions) { const { didConfirm } = (props).dialogActions; set(this, 'isShowingModal', false); @@ -341,4 +367,27 @@ export default class Notifications extends Service { onComplete && onComplete(); } } + + /** + * Renders a dialog with the full text of the last IToast instance content, + * with the option to dismiss the modal + * @memberof Notifications + */ + @action + async showContentDetail() { + const { dialogActions } = notificationDialogActionFactory(); + const { + props: { content } + } = get(this, 'toast'); + + this.notify(NotificationEvent.confirm, { + header: 'Notification Detail', + content, + dialogActions, + dismissButtonText: false, + confirmButtonText: 'Dismiss' + }); + + this.dismissToast.call(this); + } } diff --git a/wherehows-web/app/styles/components/notifications/_notification-toast.scss b/wherehows-web/app/styles/components/notifications/_notification-toast.scss index 953f397ae5..8c17bb202b 100644 --- a/wherehows-web/app/styles/components/notifications/_notification-toast.scss +++ b/wherehows-web/app/styles/components/notifications/_notification-toast.scss @@ -2,6 +2,9 @@ /// Contains the states --info, --success, --error /// Also includes a __dismiss element when applicable i.e.shown .notification-toast { + $right-index: 7; + $right-region-width: item-spacing($right-index); + position: fixed; text-align: left; border-radius: 2px; @@ -13,14 +16,14 @@ background: set-color(white, base); padding: 0; overflow: hidden; - height: 96px; - box-shadow: 0 0 0 1px rgba(0, 0, 0, .1), 0 6px 9px rgba(0, 0, 0, .2); + height: item-spacing(5) * 5; + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1), 0 6px 9px rgba(0, 0, 0, 0.2); margin-top: item-spacing(3); &__content { overflow: hidden; height: 100%; - padding: item-spacing(4 7 4 8); + padding: item-spacing(5 $right-index 5 8); &::before { content: ''; @@ -32,6 +35,12 @@ z-index: 2; } + &__msg { + overflow: hidden; + height: 100%; + margin: 0; + } + &--info { &::before { background-color: set-color(grey, light); @@ -61,7 +70,7 @@ top: 0; padding: 0; z-index: 1; - width: item-spacing(7); + width: $right-region-width; font-size: item-spacing(6); cursor: pointer; @@ -69,4 +78,12 @@ background-color: set-color(grey, light); } } + + &__content-detail { + &#{&} { + position: absolute; + bottom: item-spacing(1); + right: $right-region-width; + } + } } diff --git a/wherehows-web/app/templates/components/confirm-dialog.hbs b/wherehows-web/app/templates/components/confirm-dialog.hbs index e282a39b17..6e6fb53124 100644 --- a/wherehows-web/app/templates/components/confirm-dialog.hbs +++ b/wherehows-web/app/templates/components/confirm-dialog.hbs @@ -19,12 +19,16 @@
- + {{#unless (eq dismissButtonText false)}} + + {{/unless}} - + {{#unless (eq confirmButtonText false)}} + + {{/unless}}
{{/modal-dialog}} diff --git a/wherehows-web/app/templates/components/notifications-service.hbs b/wherehows-web/app/templates/components/notifications-service.hbs index 17521fc486..77220e68a5 100644 --- a/wherehows-web/app/templates/components/notifications-service.hbs +++ b/wherehows-web/app/templates/components/notifications-service.hbs @@ -21,7 +21,17 @@
-

{{service.toast.props.content}}

+

+ {{split-text service.toast.props.content 140}} +

+ + {{#if (gt service.toast.props.content.length 140)}} + + {{/if}}