mirror of
https://github.com/datahub-project/datahub.git
synced 2025-10-17 11:58:10 +00:00
5674 adds ability to view truncated text in notification toast as a dialog
This commit is contained in:
parent
4cf03ed9e2
commit
b47317dcd2
@ -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<void>}
|
||||
*/
|
||||
showPurgeExemptionWarning(this: DatasetCompliance): Promise<void> {
|
||||
const dialogActions = <IConfirmOptions['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<void | {}>)}
|
||||
* @returns {(Promise<void>)}
|
||||
*/
|
||||
didEditPurgePolicy(this: DatasetCompliance): Promise<void | {}> {
|
||||
didEditPurgePolicy(this: DatasetCompliance): Promise<void> {
|
||||
const { complianceType = null } = get(this, 'complianceInfo') || {};
|
||||
|
||||
if (!complianceType) {
|
||||
|
@ -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<T, K extends keyof T> {
|
||||
* @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
|
||||
? <IConfirmOptions>fleece<IConfirmOptions, 'dismissButtonText'>(['dismissButtonText'])(props)
|
||||
: props;
|
||||
resolvedProps =
|
||||
confirmButtonText === false
|
||||
? <IConfirmOptions>fleece<IConfirmOptions, 'confirmButtonText'>(['confirmButtonText'])(props)
|
||||
: props;
|
||||
|
||||
return {
|
||||
props,
|
||||
type: 'modal',
|
||||
props: resolvedProps,
|
||||
type: NotificationType.Modal,
|
||||
notificationResolution
|
||||
};
|
||||
},
|
||||
@ -223,7 +242,7 @@ const asyncDequeue = function(notificationsQueue: Array<INotification>) {
|
||||
* @param {INotification} notification
|
||||
* @param {Array<INotification>} notificationsQueue
|
||||
*/
|
||||
const enqueue = (notification: INotification, notificationsQueue: Array<INotification>) => {
|
||||
const enqueue = (notification: INotification, notificationsQueue: Array<INotification>): 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<Notifications, 'modal' | 'isShowingModal'>(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((<IToast>props).duration);
|
||||
setProperties<Notifications, 'toast' | 'isShowingToast'>(this, { toast: notification, isShowingToast: true });
|
||||
|
||||
setProperties(this, { toast: notification, isShowingToast: true });
|
||||
|
||||
if (!(<IToast>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 ((<IConfirmOptions>props).dialogActions) {
|
||||
const { didConfirm } = (<IConfirmOptions>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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,12 +19,16 @@
|
||||
</section>
|
||||
|
||||
<footer class="notification-confirm-modal__footer">
|
||||
<button class="nacho-button--large nacho-button--secondary" {{action "onClose"}}>
|
||||
{{dismissButtonText}}
|
||||
</button>
|
||||
{{#unless (eq dismissButtonText false)}}
|
||||
<button class="nacho-button--large nacho-button--secondary" {{action "onClose"}}>
|
||||
{{dismissButtonText}}
|
||||
</button>
|
||||
{{/unless}}
|
||||
|
||||
<button class="nacho-button nacho-button--large-inverse" {{action "onConfirm"}}>
|
||||
{{confirmButtonText}}
|
||||
</button>
|
||||
{{#unless (eq confirmButtonText false)}}
|
||||
<button class="nacho-button nacho-button--large-inverse" {{action "onConfirm"}}>
|
||||
{{confirmButtonText}}
|
||||
</button>
|
||||
{{/unless}}
|
||||
</footer>
|
||||
{{/modal-dialog}}
|
||||
|
@ -21,7 +21,17 @@
|
||||
<i class="notification-toast__icon"></i>
|
||||
|
||||
<div class="notification-toast__content notification-toast__content--{{service.toast.props.type}}">
|
||||
<p>{{service.toast.props.content}}</p>
|
||||
<p class="notification-toast__content__msg">
|
||||
{{split-text service.toast.props.content 140}}
|
||||
</p>
|
||||
|
||||
{{#if (gt service.toast.props.content.length 140)}}
|
||||
<button
|
||||
class="nacho-button nacho-button--tertiary notification-toast__content-detail"
|
||||
onclick={{action "showContentDetail" target=service}}>
|
||||
See Detail
|
||||
</button>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<button class="notification-toast__dismiss" onclick={{action "dismissToast" target=service}}>
|
||||
|
@ -13,7 +13,7 @@ import { Classification, ComplianceFieldIdValue, IdLogicalType } from 'wherehows
|
||||
*/
|
||||
interface IDatasetComplianceActions {
|
||||
didEditCompliancePolicy: () => Promise<void>;
|
||||
didEditPurgePolicy: () => Promise<{} | void>;
|
||||
didEditPurgePolicy: () => Promise<void>;
|
||||
didEditDatasetLevelCompliancePolicy: () => Promise<void>;
|
||||
[K: string]: (...args: Array<any>) => any;
|
||||
}
|
||||
|
44
wherehows-web/app/utils/notifications/notifications.ts
Normal file
44
wherehows-web/app/utils/notifications/notifications.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import { IConfirmOptions } from 'wherehows-web/services/notifications';
|
||||
|
||||
/**
|
||||
* Defines the interface for properties used in proxy-ing or handling dialog's
|
||||
* events async
|
||||
* @interface INotificationDialogProps
|
||||
*/
|
||||
interface INotificationDialogProps {
|
||||
dismissedOrConfirmed: Promise<void>;
|
||||
dialogActions: IConfirmOptions['dialogActions'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of INotificationDialogProps when invoked. Includes a promise for dismissing or
|
||||
* confirming the dialog
|
||||
* @returns {INotificationDialogProps}
|
||||
*/
|
||||
const notificationDialogActionFactory = (): INotificationDialogProps => {
|
||||
let dialogActions = <IConfirmOptions['dialogActions']>{};
|
||||
/**
|
||||
* Inner function to create a dialog object
|
||||
* @template T
|
||||
* @param {((value?: T | PromiseLike<T>) => void)} resolveFn
|
||||
* @param {(reason?: any) => void} rejectFn
|
||||
* @returns {IConfirmOptions.dialogActions}
|
||||
*/
|
||||
const createDialogActions = <T>(
|
||||
resolveFn: (value?: T | PromiseLike<T>) => void,
|
||||
rejectFn: (reason?: any) => void
|
||||
): IConfirmOptions['dialogActions'] => ({
|
||||
didConfirm: () => resolveFn(),
|
||||
didDismiss: () => rejectFn()
|
||||
});
|
||||
const dismissedOrConfirmed = new Promise<void>((resolve, reject): void => {
|
||||
dialogActions = { ...dialogActions, ...createDialogActions(resolve, reject) };
|
||||
});
|
||||
|
||||
return {
|
||||
dismissedOrConfirmed,
|
||||
dialogActions
|
||||
};
|
||||
};
|
||||
|
||||
export { notificationDialogActionFactory };
|
Loading…
x
Reference in New Issue
Block a user