mirror of
https://github.com/datahub-project/datahub.git
synced 2025-10-18 20:34:14 +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 { NonIdLogicalType } from 'wherehows-web/constants/datasets/compliance';
|
||||||
import { pick } from 'lodash';
|
import { pick } from 'lodash';
|
||||||
import { trackableEvent, TrackableEventCategory } from 'wherehows-web/constants/analytics/event-tracking';
|
import { trackableEvent, TrackableEventCategory } from 'wherehows-web/constants/analytics/event-tracking';
|
||||||
|
import { notificationDialogActionFactory } from 'wherehows-web/utils/notifications/notifications';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
complianceDataException,
|
complianceDataException,
|
||||||
@ -860,7 +861,7 @@ export default class DatasetCompliance extends Component {
|
|||||||
* @return {Promise<void>}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
showPurgeExemptionWarning(this: DatasetCompliance): Promise<void> {
|
showPurgeExemptionWarning(this: DatasetCompliance): Promise<void> {
|
||||||
const dialogActions = <IConfirmOptions['dialogActions']>{};
|
const { dialogActions, dismissedOrConfirmed } = notificationDialogActionFactory();
|
||||||
|
|
||||||
get(this, 'notifications').notify(NotificationEvent.confirm, {
|
get(this, 'notifications').notify(NotificationEvent.confirm, {
|
||||||
header: 'Confirm purge exemption',
|
header: 'Confirm purge exemption',
|
||||||
@ -869,10 +870,7 @@ export default class DatasetCompliance extends Component {
|
|||||||
dialogActions
|
dialogActions
|
||||||
});
|
});
|
||||||
|
|
||||||
return new Promise((resolve, reject): void => {
|
return dismissedOrConfirmed;
|
||||||
dialogActions['didConfirm'] = (): void => resolve();
|
|
||||||
dialogActions['didDismiss'] = (): void => reject();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1127,9 +1125,9 @@ export default class DatasetCompliance extends Component {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles post processing tasks after the purge policy step has been completed
|
* 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') || {};
|
const { complianceType = null } = get(this, 'complianceInfo') || {};
|
||||||
|
|
||||||
if (!complianceType) {
|
if (!complianceType) {
|
||||||
|
@ -2,6 +2,8 @@ import Service from '@ember/service';
|
|||||||
import { setProperties, get, set } from '@ember/object';
|
import { setProperties, get, set } from '@ember/object';
|
||||||
import { delay } from 'wherehows-web/utils/promise-delay';
|
import { delay } from 'wherehows-web/utils/promise-delay';
|
||||||
import { action } from '@ember-decorators/object';
|
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
|
* Flag indicating the current notification queue is being processed
|
||||||
@ -10,7 +12,7 @@ import { action } from '@ember-decorators/object';
|
|||||||
let isBuffering = false;
|
let isBuffering = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* String literal of available notifications
|
* String enum of available notifications
|
||||||
*/
|
*/
|
||||||
export enum NotificationEvent {
|
export enum NotificationEvent {
|
||||||
success = 'success',
|
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
|
* Describes the proxy handler for INotifications
|
||||||
@ -37,10 +42,15 @@ interface INotificationHandlerTrap<T, K extends keyof T> {
|
|||||||
* @interface IConfirmOptions
|
* @interface IConfirmOptions
|
||||||
*/
|
*/
|
||||||
export interface IConfirmOptions {
|
export interface IConfirmOptions {
|
||||||
|
// Header text for the confirmation dialog
|
||||||
header: string;
|
header: string;
|
||||||
|
// Content to be displayed in the confirmation dialog
|
||||||
content: string;
|
content: string;
|
||||||
dismissButtonText?: string;
|
// Text for button to dismiss dialog action, if false, button will not be rendered
|
||||||
confirmButtonText?: string;
|
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: {
|
dialogActions: {
|
||||||
didConfirm: () => any;
|
didConfirm: () => any;
|
||||||
didDismiss: () => any;
|
didDismiss: () => any;
|
||||||
@ -64,7 +74,7 @@ interface IToast {
|
|||||||
interface INotification {
|
interface INotification {
|
||||||
// The properties for the notification
|
// The properties for the notification
|
||||||
props: IConfirmOptions | IToast;
|
props: IConfirmOptions | IToast;
|
||||||
// The type if the notification
|
// The type of the notification
|
||||||
type: NotificationType;
|
type: NotificationType;
|
||||||
// Object holding the queue state for the notification
|
// Object holding the queue state for the notification
|
||||||
notificationResolution: INotificationResolver;
|
notificationResolution: INotificationResolver;
|
||||||
@ -119,7 +129,7 @@ const makeToast = (props: IToast): INotification => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
type: 'toast',
|
type: NotificationType.Toast,
|
||||||
notificationResolution
|
notificationResolution
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -131,18 +141,27 @@ const notificationHandlers: INotificationHandler = {
|
|||||||
* @return {INotification}
|
* @return {INotification}
|
||||||
*/
|
*/
|
||||||
confirm(props: IConfirmOptions): INotification {
|
confirm(props: IConfirmOptions): INotification {
|
||||||
let notificationResolution: INotificationResolver = {
|
const notificationResolution: INotificationResolver = {
|
||||||
get queueAwaiter() {
|
get queueAwaiter() {
|
||||||
return createNotificationAwaiter(this);
|
return createNotificationAwaiter(this);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set default values for button text if none are provided by consumer
|
// Set default values for button text if none are provided by consumer
|
||||||
props = { dismissButtonText: 'No', confirmButtonText: 'Yes', ...props };
|
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 {
|
return {
|
||||||
props,
|
props: resolvedProps,
|
||||||
type: 'modal',
|
type: NotificationType.Modal,
|
||||||
notificationResolution
|
notificationResolution
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -223,7 +242,7 @@ const asyncDequeue = function(notificationsQueue: Array<INotification>) {
|
|||||||
* @param {INotification} notification
|
* @param {INotification} notification
|
||||||
* @param {Array<INotification>} notificationsQueue
|
* @param {Array<INotification>} notificationsQueue
|
||||||
*/
|
*/
|
||||||
const enqueue = (notification: INotification, notificationsQueue: Array<INotification>) => {
|
const enqueue = (notification: INotification, notificationsQueue: Array<INotification>): void => {
|
||||||
notificationsQueue.unshift(notification);
|
notificationsQueue.unshift(notification);
|
||||||
asyncDequeue(notificationsQueue);
|
asyncDequeue(notificationsQueue);
|
||||||
};
|
};
|
||||||
@ -263,12 +282,14 @@ export default class Notifications extends Service {
|
|||||||
* @param {INotification} notification
|
* @param {INotification} notification
|
||||||
*/
|
*/
|
||||||
setCurrentNotification = async (notification: INotification) => {
|
setCurrentNotification = async (notification: INotification) => {
|
||||||
if (notification.type === 'modal') {
|
const { type, props } = notification;
|
||||||
setProperties<Notifications, 'modal' | 'isShowingModal'>(this, { modal: notification, isShowingModal: true });
|
|
||||||
|
if (type === NotificationType.Modal) {
|
||||||
|
setProperties(this, { modal: notification, isShowingModal: true });
|
||||||
} else {
|
} else {
|
||||||
const { props } = notification;
|
|
||||||
const toastDelay = delay((<IToast>props).duration);
|
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) {
|
if (!(<IToast>props).isSticky) {
|
||||||
await toastDelay;
|
await toastDelay;
|
||||||
@ -297,21 +318,24 @@ export default class Notifications extends Service {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the current toast from view and invokes the notification resolution resolver
|
* Removes the current toast from view and invokes the notification resolution resolver
|
||||||
|
* @memberof Notifications
|
||||||
*/
|
*/
|
||||||
@action
|
@action
|
||||||
dismissToast(this: Notifications) {
|
dismissToast() {
|
||||||
const {
|
const {
|
||||||
notificationResolution: { onComplete }
|
notificationResolution: { onComplete }
|
||||||
}: INotification = get(this, 'toast');
|
}: INotification = get(this, 'toast');
|
||||||
|
|
||||||
set(this, 'isShowingToast', false);
|
set(this, 'isShowingToast', false);
|
||||||
onComplete && onComplete();
|
onComplete && onComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ignores the modal, invokes the user supplied didDismiss callback
|
* Ignores the modal, invokes the user supplied didDismiss callback
|
||||||
|
* @memberof Notifications
|
||||||
*/
|
*/
|
||||||
@action
|
@action
|
||||||
dismissModal(this: Notifications) {
|
dismissModal() {
|
||||||
const {
|
const {
|
||||||
props,
|
props,
|
||||||
notificationResolution: { onComplete }
|
notificationResolution: { onComplete }
|
||||||
@ -327,13 +351,15 @@ export default class Notifications extends Service {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Confirms the dialog and invokes the user supplied didConfirm callback
|
* Confirms the dialog and invokes the user supplied didConfirm callback
|
||||||
|
* @memberof Notifications
|
||||||
*/
|
*/
|
||||||
@action
|
@action
|
||||||
confirmModal(this: Notifications) {
|
confirmModal() {
|
||||||
const {
|
const {
|
||||||
props,
|
props,
|
||||||
notificationResolution: { onComplete }
|
notificationResolution: { onComplete }
|
||||||
}: INotification = get(this, 'modal');
|
}: INotification = get(this, 'modal');
|
||||||
|
|
||||||
if ((<IConfirmOptions>props).dialogActions) {
|
if ((<IConfirmOptions>props).dialogActions) {
|
||||||
const { didConfirm } = (<IConfirmOptions>props).dialogActions;
|
const { didConfirm } = (<IConfirmOptions>props).dialogActions;
|
||||||
set(this, 'isShowingModal', false);
|
set(this, 'isShowingModal', false);
|
||||||
@ -341,4 +367,27 @@ export default class Notifications extends Service {
|
|||||||
onComplete && onComplete();
|
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
|
/// Contains the states --info, --success, --error
|
||||||
/// Also includes a __dismiss element when applicable i.e.shown
|
/// Also includes a __dismiss element when applicable i.e.shown
|
||||||
.notification-toast {
|
.notification-toast {
|
||||||
|
$right-index: 7;
|
||||||
|
$right-region-width: item-spacing($right-index);
|
||||||
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
@ -13,14 +16,14 @@
|
|||||||
background: set-color(white, base);
|
background: set-color(white, base);
|
||||||
padding: 0;
|
padding: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 96px;
|
height: item-spacing(5) * 5;
|
||||||
box-shadow: 0 0 0 1px rgba(0, 0, 0, .1), 0 6px 9px rgba(0, 0, 0, .2);
|
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);
|
margin-top: item-spacing(3);
|
||||||
|
|
||||||
&__content {
|
&__content {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding: item-spacing(4 7 4 8);
|
padding: item-spacing(5 $right-index 5 8);
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
content: '';
|
content: '';
|
||||||
@ -32,6 +35,12 @@
|
|||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__msg {
|
||||||
|
overflow: hidden;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
&--info {
|
&--info {
|
||||||
&::before {
|
&::before {
|
||||||
background-color: set-color(grey, light);
|
background-color: set-color(grey, light);
|
||||||
@ -61,7 +70,7 @@
|
|||||||
top: 0;
|
top: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
width: item-spacing(7);
|
width: $right-region-width;
|
||||||
font-size: item-spacing(6);
|
font-size: item-spacing(6);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
@ -69,4 +78,12 @@
|
|||||||
background-color: set-color(grey, light);
|
background-color: set-color(grey, light);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__content-detail {
|
||||||
|
&#{&} {
|
||||||
|
position: absolute;
|
||||||
|
bottom: item-spacing(1);
|
||||||
|
right: $right-region-width;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,12 +19,16 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<footer class="notification-confirm-modal__footer">
|
<footer class="notification-confirm-modal__footer">
|
||||||
<button class="nacho-button--large nacho-button--secondary" {{action "onClose"}}>
|
{{#unless (eq dismissButtonText false)}}
|
||||||
{{dismissButtonText}}
|
<button class="nacho-button--large nacho-button--secondary" {{action "onClose"}}>
|
||||||
</button>
|
{{dismissButtonText}}
|
||||||
|
</button>
|
||||||
|
{{/unless}}
|
||||||
|
|
||||||
<button class="nacho-button nacho-button--large-inverse" {{action "onConfirm"}}>
|
{{#unless (eq confirmButtonText false)}}
|
||||||
{{confirmButtonText}}
|
<button class="nacho-button nacho-button--large-inverse" {{action "onConfirm"}}>
|
||||||
</button>
|
{{confirmButtonText}}
|
||||||
|
</button>
|
||||||
|
{{/unless}}
|
||||||
</footer>
|
</footer>
|
||||||
{{/modal-dialog}}
|
{{/modal-dialog}}
|
||||||
|
@ -21,7 +21,17 @@
|
|||||||
<i class="notification-toast__icon"></i>
|
<i class="notification-toast__icon"></i>
|
||||||
|
|
||||||
<div class="notification-toast__content notification-toast__content--{{service.toast.props.type}}">
|
<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>
|
</div>
|
||||||
|
|
||||||
<button class="notification-toast__dismiss" onclick={{action "dismissToast" target=service}}>
|
<button class="notification-toast__dismiss" onclick={{action "dismissToast" target=service}}>
|
||||||
|
@ -13,7 +13,7 @@ import { Classification, ComplianceFieldIdValue, IdLogicalType } from 'wherehows
|
|||||||
*/
|
*/
|
||||||
interface IDatasetComplianceActions {
|
interface IDatasetComplianceActions {
|
||||||
didEditCompliancePolicy: () => Promise<void>;
|
didEditCompliancePolicy: () => Promise<void>;
|
||||||
didEditPurgePolicy: () => Promise<{} | void>;
|
didEditPurgePolicy: () => Promise<void>;
|
||||||
didEditDatasetLevelCompliancePolicy: () => Promise<void>;
|
didEditDatasetLevelCompliancePolicy: () => Promise<void>;
|
||||||
[K: string]: (...args: Array<any>) => any;
|
[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