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

Commit a13f863

Browse files
feat: Adds more slots to the modal + makes it more robust
1 parent 3f034c7 commit a13f863

5 files changed

Lines changed: 108 additions & 107 deletions

File tree

docs/src/api/_baseline.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,22 @@
3434
},
3535
"FdModal": {
3636
"slots": {
37+
"footer": {
38+
"name": "footer",
39+
"description": "Modal Footer"
40+
},
41+
"actions": {
42+
"name": "actions",
43+
"description": "Modal Actions"
44+
},
45+
"close": {
46+
"name": "close",
47+
"description": "Modal Close Button"
48+
},
49+
"title": {
50+
"name": "title",
51+
"description": "Modal Title"
52+
},
3753
"": {
3854
"name": "",
3955
"description": "Modal Body"

docs/src/pages/Modal/Example.vue

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,26 @@
22

33
<template>
44
<div>
5-
<FdButton @click.stop="showModal">Show Modal</FdButton>
6-
<FdModal title="Modal Title" :active.sync="isModalActive">
5+
<FdButton @click="show">Show Modal</FdButton>
6+
<FdModal title="Modal Title" :active="active" @close="close">
77
<p>Do you want to invite your friends to join the party?</p>
8-
<template slot="actions">
9-
<FdButton @click="closeModal" styling="light">Cancel</FdButton>
10-
<FdButton @click="closeModal" styling="emphasized"
11-
>Invite Friends</FdButton
12-
>
8+
<template #actions>
9+
<FdButton @click="close" styling="light">Cancel</FdButton>
10+
<FdButton @click="close" styling="emphasized">Invite</FdButton>
1311
</template>
1412
</FdModal>
1513
</div>
1614
</template>
1715

1816
<script>
1917
export default {
20-
data() {
21-
return {
22-
isModalActive: false
23-
};
24-
},
18+
data: () => ({ active: false }),
2519
methods: {
26-
closeModal() {
27-
this.isModalActive = false;
20+
close() {
21+
this.active = false;
2822
},
29-
showModal() {
30-
this.isModalActive = true;
23+
show() {
24+
this.active = true;
3125
}
3226
}
3327
};

ui/src/components/ClickAwayContainer.ts

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -52,31 +52,36 @@ export default Vue.extend({
5252
}
5353
}
5454
this.$emit(CLICK_OUTSIDE_EVENT, event);
55+
},
56+
activeDidChange(isActive: boolean, wasActive: boolean) {
57+
const { documentElement } = document;
58+
// We are listening for clicks on the documentElement. Listening for clicks
59+
// on the body elements also works but less reliably. For example if the height
60+
// the body element is < 100% there will be places on the page which will
61+
// emit nothing when clicked.
62+
if (documentElement == null) {
63+
warn(
64+
`v-${this}: Cannot do anything without a documentElement element.`
65+
);
66+
return;
67+
}
68+
if (isActive && !wasActive) {
69+
this.$el.addEventListener("click", this.click, false);
70+
documentElement.addEventListener("click", this.click, false);
71+
}
72+
if (!isActive && wasActive) {
73+
this.$el.removeEventListener("click", this.click, false);
74+
documentElement.removeEventListener("click", this.click, false);
75+
}
5576
}
5677
},
5778
watch: {
5879
active: {
5980
immediate: true,
6081
handler(isActive: boolean, wasActive: boolean): void {
61-
const { documentElement } = document;
62-
// We are listening for clicks on the documentElement. Listening for clicks
63-
// on the body elements also works but less reliably. For example if the height
64-
// the body element is < 100% there will be places on the page which will
65-
// emit nothing when clicked.
66-
if (documentElement == null) {
67-
warn(
68-
`v-${this}: Cannot do anything without a documentElement element.`
69-
);
70-
return;
71-
}
72-
if (isActive && !wasActive) {
73-
this.$el.addEventListener("click", this.click, false);
74-
documentElement.addEventListener("click", this.click, false);
75-
}
76-
if (!isActive && wasActive) {
77-
this.$el.removeEventListener("click", this.click, false);
78-
documentElement.removeEventListener("click", this.click, false);
79-
}
82+
setTimeout(() => {
83+
this.activeDidChange(isActive, wasActive);
84+
});
8085
}
8186
}
8287
}

ui/src/components/Modal/Modal.vue

Lines changed: 56 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,67 @@
11
<template>
2-
<transition name="fade">
3-
<div
4-
v-show="isActive"
5-
class="fd-ui__overlay fd-overlay fd-overlay--modal"
6-
:aria-hidden="!isActive"
2+
<div
3+
v-show="active"
4+
class="fd-ui__overlay fd-overlay fd-overlay--modal"
5+
:aria-hidden="String(!active)"
6+
>
7+
<ClickAwayContainer
8+
@clickOutside="close"
9+
class="fd-modal"
10+
:tabindex="active ? -1 : 0"
11+
:active="active"
712
>
8-
<ClickAwayContainer
9-
@clickOutside="clickOutside"
10-
class="fd-modal"
11-
:tabindex="isActive ? -1 : 0"
12-
:active="isActive"
13-
>
14-
<div class="fd-modal__content" role="document">
15-
<!-- HEADER -->
16-
<div class="fd-modal__header">
17-
<h1 class="fd-modal__title">{{ title }}</h1>
18-
<Button
13+
<div class="fd-modal__content" role="document">
14+
<!-- HEADER -->
15+
<div class="fd-modal__header">
16+
<slot name="title">
17+
<h1 class="fd-modal__title">
18+
{{ title }}
19+
</h1>
20+
</slot>
21+
<slot name="close">
22+
<FdButton
1923
class="fd-modal__close"
2024
styling="light"
2125
@click="close"
2226
aria-label="close"
2327
/>
24-
</div>
25-
26-
<!-- BODY -->
27-
<div class="fd-modal__body">
28-
<slot />
29-
</div>
28+
</slot>
29+
</div>
3030

31-
<!-- FOOTER -->
32-
<footer
33-
v-if="$slots.footer != null || $slots.actions != null"
34-
class="fd-modal__footer"
35-
>
36-
<slot name="footer" />
37-
<div v-if="$slots.actions" class="fd-modal__actions">
38-
<slot name="actions" />
39-
</div>
40-
</footer>
31+
<!-- BODY -->
32+
<div class="fd-modal__body">
33+
<slot />
4134
</div>
42-
</ClickAwayContainer>
43-
</div>
44-
</transition>
35+
36+
<!-- FOOTER -->
37+
<footer
38+
v-if="$slots.footer != null || $slots.actions != null"
39+
class="fd-modal__footer"
40+
>
41+
<slot name="footer" />
42+
<div v-if="$slots.actions" class="fd-modal__actions">
43+
<slot name="actions" />
44+
</div>
45+
</footer>
46+
</div>
47+
</ClickAwayContainer>
48+
</div>
4549
</template>
4650

4751
<script lang="ts">
48-
// Use these types in order to cast your props. Delete if not needed.
49-
// import { PropValidator } from "vue/types/options";
50-
// import { Prop } from "vue/types/options";
5152
import ClickAwayContainer from "@/components/ClickAwayContainer";
52-
import { Button } from "@/components/Button";
53+
import FdButton from "@/components/Button";
5354
import { FocusTrap, mixins } from "@/mixins";
5455
5556
export default mixins(FocusTrap).extend({
5657
name: "FdModal",
58+
components: { FdButton, ClickAwayContainer },
59+
methods: {
60+
close() {
61+
this.$emit("close");
62+
this.$emit("update:active", false);
63+
}
64+
},
5765
mounted() {
5866
document.body.appendChild(this.$el);
5967
this.initializeFocusTrap(this.$el, {
@@ -62,16 +70,18 @@ export default mixins(FocusTrap).extend({
6270
});
6371
},
6472
destroyed() {
65-
const el = this.$el;
66-
if (el != null) {
67-
const parent = el.parentNode;
68-
if (parent != null) {
69-
parent.removeChild(el);
70-
}
73+
const { $el } = this;
74+
if ($el == null) {
75+
return;
76+
}
77+
const parent = $el.parentNode;
78+
if (parent == null) {
79+
return;
7180
}
81+
parent.removeChild($el);
7282
},
7383
updated() {
74-
if (this.isActive) {
84+
if (this.active) {
7585
this.activateFocusTrap();
7686
} else {
7787
this.deactivateFocusTrap();
@@ -80,31 +90,6 @@ export default mixins(FocusTrap).extend({
8090
props: {
8191
active: { type: Boolean, default: false },
8292
title: { type: String, default: null }
83-
},
84-
components: { Button, ClickAwayContainer },
85-
watch: {
86-
active: {
87-
immediate: true,
88-
handler(newIsActive: boolean) {
89-
this.isActive = newIsActive;
90-
}
91-
}
92-
},
93-
methods: {
94-
clickOutside() {
95-
if (this.isActive) {
96-
this.close();
97-
}
98-
},
99-
close() {
100-
this.$emit("close");
101-
this.$emit("update:active", false);
102-
}
103-
},
104-
data() {
105-
return {
106-
isActive: this.active
107-
};
10893
}
10994
});
11095
</script>

ui/src/components/Modal/__tests__/__snapshots__/Modal.test.ts.snap

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ exports[`Modal renders correctly 1`] = `
44
<div
55
aria-hidden="true"
66
class="fd-ui__overlay fd-overlay fd-overlay--modal"
7-
name="fade"
87
style="display: none;"
98
>
109
<div
@@ -22,6 +21,8 @@ exports[`Modal renders correctly 1`] = `
2221
class="fd-modal__title"
2322
>
2423
24+
25+
2526
</h1>
2627
2728
<button

0 commit comments

Comments
 (0)