Skip to content
This repository was archived by the owner on Aug 2, 2024. It is now read-only.

Commit e73a4f4

Browse files
authored
fix alert dismissible class (#520)
update alert component test snapshot add notifications component and fdNotifications installation update documentation remove unnecessary checks unsubscribe to events on beforeDestroy updated documentation fixed deleting by group updated documentation and example fix: group name with value '' was clearing all groups in hideAll separated styles from sfc file call onDismiss callback once notification is removed update documentation: styles + onDismiss
1 parent 14d0999 commit e73a4f4

File tree

13 files changed

+813
-5
lines changed

13 files changed

+813
-5
lines changed

src/components/Alert/Alert.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export default {
6969
computed: {
7070
classes() {
7171
const type = this.type === "default" ? [] : [`fd-alert--${this.type}`];
72-
const dismissible = this.dismissible ? [] : ["fd-alert--dismissible"];
72+
const dismissible = this.dismissible ? ["fd-alert--dismissible"] : [];
7373
return ["fd-alert", ...type, ...dismissible];
7474
}
7575
},

src/components/Alert/__tests__/Alert.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ describe("Alert", () => {
1717
expect(warningAlert.element).toMatchSnapshot();
1818
expect(errorAlert.element).toMatchSnapshot();
1919
expect(warningAlert.classes("fd-alert--warning")).toBe(true);
20-
expect(errorAlert.classes("fd-alert--dismissible")).toBe(true);
20+
expect(errorAlert.classes("fd-alert--dismissible")).toBe(false);
2121
});
2222
it("renders default slot when passed", () => {
2323
const dummySlot = "Slot text";

src/components/Alert/__tests__/__snapshots__/Alert.test.js.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
exports[`Alert renders correctly 1`] = `
44
<div
5-
class="fd-alert fd-alert--warning"
5+
class="fd-alert fd-alert--warning fd-alert--dismissible"
66
id="warningAlert"
77
name="fade"
88
role="alert"
@@ -18,7 +18,7 @@ exports[`Alert renders correctly 1`] = `
1818

1919
exports[`Alert renders correctly 2`] = `
2020
<div
21-
class="fd-alert fd-alert--error fd-alert--dismissible"
21+
class="fd-alert fd-alert--error"
2222
id="errorAlert"
2323
name="fade"
2424
role="alert"

src/components/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ import TimePicker from "./TimePicker";
5959
import Token from "./Token";
6060
import Tree from "./tree";
6161
import VirtualizedList from "./VirtualizedList";
62+
import Notifications from "./notifications";
63+
import { FdNotificationsManager } from "./notifications";
6264

6365
const plugin = {
6466
install(vue, options) {
@@ -100,6 +102,7 @@ const plugin = {
100102
MenuPopover,
101103
Modal,
102104
ModalOverlay,
105+
Notifications,
103106
ObjectTree,
104107
Pagination,
105108
Panel,
@@ -126,6 +129,7 @@ const plugin = {
126129
VirtualizedList
127130
];
128131
plugins.forEach(plugin => vue.use(plugin, options));
132+
vue.use(FdNotificationsManager);
129133
}
130134
};
131135
export default plugin;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import Vue from "vue";
2+
3+
export const eventsBus = new Vue();
4+
5+
export const events = {
6+
show: "show",
7+
hide: "hide",
8+
hideAll: "hideAll"
9+
};
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
<template>
2+
<div :class="computedContainerClasses" :style="computedContainerStyles">
3+
<fd-alert
4+
:key="item.id"
5+
:type="item.type"
6+
:dismissible="false"
7+
:class="item.animation.class"
8+
v-for="item in notifications"
9+
@click.native="onManualDismiss(item)"
10+
>
11+
<slot>
12+
<div v-if="item.title" class="fd-notification__title" v-html="item.title"></div>
13+
<div class="fd-notification__content" v-html="item.content"></div>
14+
</slot>
15+
</fd-alert>
16+
</div>
17+
</template>
18+
19+
<script>
20+
import FdAlert from "../Alert/Alert.vue";
21+
import { eventsBus, events } from "./EventsBus";
22+
23+
/** @typedef { import('./notifications.types').NewNotificationOpts } NewNotificationOpts */
24+
25+
const config = {
26+
defaultTimeout: 2000,
27+
animationDuration: 400,
28+
fadeInAnimationClass: "fadeIn",
29+
fadeOutAnimationClass: "fadeOut"
30+
};
31+
32+
export default {
33+
name: "FdNotifications",
34+
components: { FdAlert },
35+
props: {
36+
group: {
37+
// the name of this notifications group, this will allow the
38+
// user to have multiple notifications groups for easier control
39+
type: String,
40+
default: ""
41+
},
42+
position: {
43+
// array of 2 elements to specify the position of this group
44+
type: Array,
45+
default: () => ["top", "right"],
46+
validator: value => {
47+
return (
48+
value.length === 2 &&
49+
["bottom", "top"].indexOf(value[0]) >= 0 &&
50+
["left", "right", "center"].indexOf(value[1]) >= 0
51+
);
52+
}
53+
},
54+
customStyles: {
55+
// custom styles that will be applied to the container
56+
type: Object,
57+
default: () => ({})
58+
}
59+
},
60+
data() {
61+
return {
62+
notifications: [],
63+
defaultStyles: {
64+
maxWidth: "400px"
65+
}
66+
};
67+
},
68+
methods: {
69+
/** Create the required notification object and adds it to the list
70+
* @param {NewNotificationOpts} opts
71+
*/
72+
addNotification(opts) {
73+
if (!this.isTargetGroup(opts)) {
74+
return;
75+
}
76+
const notification = {
77+
id: opts.id,
78+
type: opts.type,
79+
title: opts.title,
80+
content: opts.content,
81+
onDismiss: opts.onDismiss,
82+
dismissible: opts.dismissible || true,
83+
animation: {
84+
class: config.fadeInAnimationClass
85+
}
86+
};
87+
88+
if (opts.timeout || !opts.permanent) {
89+
notification.timeout = config.timeout || config.defaultTimeout;
90+
notification.timer = setTimeout(() => {
91+
this.destroyNotification(notification);
92+
}, notification.timeout);
93+
} else {
94+
notification.permanent = true;
95+
}
96+
this.notifications.push(notification);
97+
},
98+
/**
99+
* Destroy the notification on user click in cases it is dismissable
100+
*/
101+
onManualDismiss(notification) {
102+
if (notification.dismissible) {
103+
this.destroyNotification(notification);
104+
}
105+
},
106+
/**
107+
* Hide the notification and do the required cleanup
108+
*/
109+
destroyNotification(notification) {
110+
notification.animation.class = config.fadeOutAnimationClass;
111+
112+
// hide notification timer if it's not permanent
113+
if (!notification.permanent && notification.timer) {
114+
clearTimeout(notification.timer);
115+
}
116+
// wait for animation to finish and remove the notification from the list
117+
setTimeout(() => {
118+
this.notifications = this.notifications.filter(n => n.id !== notification.id);
119+
if (notification.onDismiss && typeof notification.onDismiss === "function") {
120+
notification.onDismiss(notification);
121+
}
122+
}, config.animationDuration);
123+
},
124+
/**
125+
* Remove all the notifications
126+
*/
127+
dismissAll() {
128+
this.notifications.forEach(this.destroyNotification);
129+
},
130+
/**
131+
* In case an existing id was passed, clear the notification that has it
132+
*/
133+
dismissById(id) {
134+
const notification = this.notifications.find(n => n.id === id);
135+
if (notification) {
136+
this.destroyNotification(notification);
137+
}
138+
},
139+
/**
140+
* Check if this is the correct notifications group
141+
*/
142+
isTargetGroup(opts) {
143+
opts.group = opts.group || "";
144+
return opts.group === this.group;
145+
},
146+
/**
147+
* Called when the bus emits the hideAll event
148+
*/
149+
onHideAll(group) {
150+
if (group !== null && group !== undefined && !this.isTargetGroup({ group })) {
151+
return;
152+
}
153+
this.dismissAll();
154+
},
155+
/**
156+
* Called when the bus emits the hide event
157+
*/
158+
onHideSingle(opts) {
159+
if (!this.isTargetGroup(opts)) {
160+
return;
161+
}
162+
this.dismissById(opts.id);
163+
}
164+
},
165+
computed: {
166+
computedContainerClasses() {
167+
return [
168+
"fd-notifications__group",
169+
`fd-notifications__group--${this.position[0]}`,
170+
`fd-notifications__group--${this.position[1]}`
171+
];
172+
},
173+
computedContainerStyles() {
174+
return { ...this.defaultStyles, ...this.customStyles };
175+
}
176+
},
177+
mounted() {
178+
eventsBus.$on(events.hide, this.onHideSingle);
179+
eventsBus.$on(events.hideAll, this.onHideAll);
180+
eventsBus.$on(events.show, this.addNotification);
181+
},
182+
beforeDestroy() {
183+
eventsBus.$off(events.hide, this.onHideSingle);
184+
eventsBus.$off(events.hideAll, this.onHideAll);
185+
eventsBus.$off(events.show, this.addNotification);
186+
}
187+
};
188+
</script>
189+
190+
<style lang="scss">
191+
@import "_notifications.styles";
192+
@import "_notifications.animations";
193+
</style>

0 commit comments

Comments
 (0)