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

Commit b4783c7

Browse files
feat: added split button component
1 parent bb4e5eb commit b4783c7

File tree

12 files changed

+522
-2
lines changed

12 files changed

+522
-2
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<template>
2+
<div class="fd-button-split" role="group">
3+
<!-- Provide a custom action button. The action button is bigger button with the text that should trigger some kind of action. You can use fd-split-button-action to render the default auxiliary-button and customize it. -->
4+
<slot v-bind="slotProps" name="action">
5+
<fd-split-button-action @click="$emit('click')">
6+
<!-- Provide a title. This is rendered inside the 'action' part of the split button (big area on the left). -->
7+
<slot />
8+
</fd-split-button-action>
9+
</slot>
10+
<!-- Provide a custom auxiliary button. The auxiliary button should bring up some kind of menu/popover. You can use fd-split-button-auxiliary to render the default auxiliary-button and customize it. -->
11+
<slot v-bind="slotProps" name="auxiliary">
12+
<fd-split-button-auxiliary @click="$emit('click:auxiliary')" />
13+
</slot>
14+
</div>
15+
</template>
16+
17+
<script>
18+
import FdSplitButtonAuxiliary from "./../SplitButtonAuxiliary/SplitButtonAuxiliary.vue";
19+
import FdSplitButtonAction from "./../SplitButtonAction/SplitButtonAction.vue";
20+
import Vue from "vue";
21+
import ButtonTypes from "./../Button/ButtonTypes";
22+
import IconMixin from "./../../mixins/Icon";
23+
24+
export default {
25+
name: "FdSplitButton",
26+
mixins: [IconMixin],
27+
components: {
28+
FdSplitButtonAction,
29+
FdSplitButtonAuxiliary
30+
},
31+
provide() {
32+
return {
33+
splitButton: this.splitButton
34+
};
35+
},
36+
computed: {
37+
slotProps() {
38+
return this.splitButton;
39+
}
40+
},
41+
props: {
42+
compact: Boolean,
43+
styling: {
44+
type: String,
45+
default: null,
46+
validator: value => ["emphasized", "light"].indexOf(value) >= 0
47+
},
48+
state: {
49+
type: String,
50+
default: "normal",
51+
validator: value => ["normal", "selected", "disabled"].indexOf(value) >= 0
52+
},
53+
type: {
54+
type: String,
55+
default: null,
56+
validator: value => ButtonTypes.indexOf(value) >= 0
57+
}
58+
},
59+
watch: {
60+
state(state) {
61+
this.splitButton.state = state;
62+
},
63+
type(type) {
64+
this.splitButton.type = type;
65+
},
66+
styling(styling) {
67+
this.splitButton.styling = styling;
68+
},
69+
compact(compact) {
70+
this.splitButton.compact = compact;
71+
},
72+
icon(icon) {
73+
this.splitButton.icon = icon;
74+
}
75+
},
76+
data() {
77+
return {
78+
splitButton: Vue.observable({
79+
state: this.state,
80+
type: this.type,
81+
styling: this.styling,
82+
compact: this.compact,
83+
icon: this.icon
84+
})
85+
};
86+
}
87+
};
88+
</script>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`Split Button renders correctly 1`] = `
4+
<div
5+
class="fd-button-split"
6+
role="group"
7+
>
8+
<button
9+
class="fd-button"
10+
>
11+
Hello World
12+
</button>
13+
14+
<button
15+
aria-label="More"
16+
class="fd-button sap-icon--slim-arrow-down fd-button"
17+
/>
18+
</div>
19+
`;
20+
21+
exports[`Split Button when disabled renders correctly 1`] = `
22+
<div
23+
class="fd-button-split"
24+
role="group"
25+
>
26+
<button
27+
class="fd-button is-disabled"
28+
disabled="disabled"
29+
>
30+
Hello World
31+
</button>
32+
33+
<button
34+
aria-label="More"
35+
class="fd-button sap-icon--slim-arrow-down fd-button is-disabled"
36+
disabled="disabled"
37+
/>
38+
</div>
39+
`;
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { mount, createLocalVue } from "@vue/test-utils";
2+
import FundamentalVue from "./../../../";
3+
import FdSplitButton from "./../SplitButton.vue";
4+
import FdSplitButtonAction from "./../../SplitButtonAction/SplitButtonAction.vue";
5+
import FdSplitButtonAuxiliary from "./../../SplitButtonAuxiliary/SplitButtonAuxiliary.vue";
6+
7+
const createSplitButton = ({ propsData } = { propsData: {} }) => {
8+
const localVue = createLocalVue();
9+
localVue.use(FundamentalVue);
10+
return mount(FdSplitButton, {
11+
localVue,
12+
propsData,
13+
slots: {
14+
default: "Hello World"
15+
}
16+
});
17+
};
18+
19+
describe("Split Button", () => {
20+
test("renders correctly", () => {
21+
expect(createSplitButton().element).toMatchSnapshot();
22+
});
23+
24+
test("emits click-event when action-button is clicked", () => {
25+
const wrapper = createSplitButton();
26+
const actionButtonWrapper = wrapper.find(FdSplitButtonAction);
27+
expect(actionButtonWrapper.exists()).toBe(true);
28+
actionButtonWrapper.trigger("click");
29+
expect(wrapper.emitted("click")).toHaveLength(1);
30+
});
31+
32+
test("emits click:auxiliary-event when auxiliary-button is clicked", () => {
33+
const wrapper = createSplitButton();
34+
const auxiliaryButtonWrapper = wrapper.find(FdSplitButtonAuxiliary);
35+
expect(auxiliaryButtonWrapper.exists()).toBe(true);
36+
auxiliaryButtonWrapper.trigger("click");
37+
expect(wrapper.emitted("click:auxiliary")).toHaveLength(1);
38+
expect(wrapper.emitted("click")).toBeUndefined();
39+
});
40+
41+
describe("when disabled", () => {
42+
/** @type {import("@vue/test-utils").Wrapper<Vue>} */
43+
let disabledWrapper;
44+
beforeEach(() => {
45+
disabledWrapper = createSplitButton({
46+
propsData: {
47+
state: "disabled"
48+
}
49+
});
50+
});
51+
52+
test("renders correctly", () => {
53+
expect(disabledWrapper.element).toMatchSnapshot();
54+
});
55+
56+
test("click:auxiliary-event is not emitted when auxiliary-button is clicked", () => {
57+
const auxiliaryButtonWrapper = disabledWrapper.find(
58+
FdSplitButtonAuxiliary
59+
);
60+
expect(auxiliaryButtonWrapper.exists()).toBe(true);
61+
auxiliaryButtonWrapper.trigger("click");
62+
expect(disabledWrapper.emitted("click:auxiliary")).toBeUndefined();
63+
expect(disabledWrapper.emitted("click")).toBeUndefined();
64+
});
65+
66+
test("click-event is not emitted when action-button is clicked", () => {
67+
const actionButtonWrapper = disabledWrapper.find(FdSplitButtonAction);
68+
expect(actionButtonWrapper.exists()).toBe(true);
69+
actionButtonWrapper.trigger("click");
70+
expect(disabledWrapper.emitted("click:auxiliary")).toBeUndefined();
71+
expect(disabledWrapper.emitted("click")).toBeUndefined();
72+
});
73+
});
74+
});
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import SplitButton from "./SplitButton.vue";
2+
import { pluginify } from "./../../util";
3+
export default pluginify(SplitButton);
4+
export { SplitButton };
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<template>
2+
<fd-button
3+
v-bind="$attrs"
4+
v-on="$listeners"
5+
:state="state"
6+
:type="type"
7+
:styling="styling"
8+
:compact="compact"
9+
:icon="icon"
10+
>
11+
<slot />
12+
</fd-button>
13+
</template>
14+
15+
<script>
16+
import FdButton from "./../Button/Button.vue";
17+
18+
export default {
19+
name: "FdSplitButtonAction",
20+
components: { FdButton },
21+
inject: ["splitButton"],
22+
computed: {
23+
state() {
24+
return this.splitButton.state;
25+
},
26+
type() {
27+
return this.splitButton.type;
28+
},
29+
styling() {
30+
return this.splitButton.styling;
31+
},
32+
compact() {
33+
return this.splitButton.disacompactbled;
34+
},
35+
icon() {
36+
return this.splitButton.icon;
37+
}
38+
}
39+
};
40+
</script>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import SplitButtonAction from "./SplitButtonAction.vue";
2+
import { pluginify } from "./../../util";
3+
export default pluginify(SplitButtonAction);
4+
export { SplitButtonAction };
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<template>
2+
<fd-button
3+
class="fd-button sap-icon--slim-arrow-down"
4+
aria-label="More"
5+
:state="state"
6+
:type="type"
7+
:styling="styling"
8+
:compact="compact"
9+
v-bind="$attrs"
10+
v-on="$listeners"
11+
><slot
12+
/></fd-button>
13+
</template>
14+
15+
<script>
16+
import FdButton from "./../Button/Button.vue";
17+
18+
export default {
19+
name: "FdSplitButtonAuxiliary",
20+
components: { FdButton },
21+
inject: ["splitButton"],
22+
computed: {
23+
state() {
24+
return this.splitButton.state;
25+
},
26+
type() {
27+
return this.splitButton.type;
28+
},
29+
styling() {
30+
return this.splitButton.styling;
31+
},
32+
compact() {
33+
return this.splitButton.disacompactbled;
34+
}
35+
}
36+
};
37+
</script>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import SplitButtonAuxiliary from "./SplitButtonAuxiliary.vue";
2+
import { pluginify } from "./../../util";
3+
export default pluginify(SplitButtonAuxiliary);
4+
export { SplitButtonAuxiliary };

src/components/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ import SearchInput from "./SearchInput";
3131
import SideNav from "./SideNav";
3232
import Spinner from "./Spinner";
3333
import Status from "./Status";
34+
import SplitButton from "./SplitButton";
35+
import SplitButtonAuxiliary from "./SplitButtonAuxiliary";
36+
import SplitButtonAction from "./SplitButtonAction";
3437
import Table from "./Table";
3538
import Tabs from "./Tabs";
3639
import Tile from "./Tile";
@@ -75,6 +78,9 @@ const plugin = {
7578
SideNav,
7679
Spinner,
7780
Status,
81+
SplitButton,
82+
SplitButtonAuxiliary,
83+
SplitButtonAction,
7884
Table,
7985
Tabs,
8086
Tile,

0 commit comments

Comments
 (0)