Skip to content

Commit 4b32f9d

Browse files
authored
Components vue composition api (#25)
1 parent 5ef3a43 commit 4b32f9d

File tree

33 files changed

+997
-666
lines changed

33 files changed

+997
-666
lines changed

src/components/primitives/Autocomplete/Autocomplete.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ const onChange = () => {
4141
state.isOpen = true;
4242
};
4343
44-
const setResult = (result) => {
44+
const setResult = (result: string | number) => {
4545
search.value = result;
4646
state.isOpen = false;
4747
};

src/components/primitives/Button/Button.vue

Lines changed: 41 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,46 @@
1-
<script>
2-
import { computed } from "vue";
1+
<script setup lang="ts">
2+
import { computed, useAttrs } from "vue";
33
4-
export default {
5-
name: "VButton",
6-
props: {
7-
type: {
8-
type: String,
9-
default: "is-primary",
10-
},
11-
size: String,
12-
label: String,
13-
rounded: Boolean,
14-
loading: Boolean,
15-
outlined: Boolean,
16-
expanded: Boolean,
17-
inverted: Boolean,
18-
focused: Boolean,
19-
active: Boolean,
20-
hovered: Boolean,
21-
selected: Boolean,
22-
nativeType: {
23-
type: String,
24-
default: "button",
25-
},
26-
tag: {
27-
type: String,
28-
default: "button",
29-
},
30-
light: Boolean,
4+
const props = withDefaults(
5+
defineProps<{
6+
type?:
7+
| "is-white"
8+
| "is-light"
9+
| "is-dark"
10+
| "is-black"
11+
| "is-text"
12+
| "is-primary"
13+
| "is-link"
14+
| "is-info"
15+
| "is-success"
16+
| "is-warning"
17+
| "is-danger";
18+
size?: "is-small" | "is-normal" | "is-medium" | "is-large";
19+
label?: string;
20+
rounded?: boolean;
21+
loading?: boolean;
22+
outlined?: boolean;
23+
expanded?: boolean;
24+
inverted?: boolean;
25+
focused?: boolean;
26+
active?: boolean;
27+
hovered?: boolean;
28+
selected?: boolean;
29+
// https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/button#type
30+
nativeType?: "button" | "submit" | "reset";
31+
tag?: string;
32+
light?: boolean;
33+
}>(),
34+
{
35+
type: "is-primary",
36+
nativeType: "button",
37+
tag: "button",
3138
},
32-
setup(props, { attrs }) {
33-
const computedTag = computed(() => (attrs.disabled ? "button" : props.tag));
34-
return { computedTag };
35-
},
36-
};
39+
);
40+
41+
const attrs = useAttrs();
42+
43+
const computedTag = computed(() => (attrs.disabled ? "button" : props.tag));
3744
</script>
3845

3946
<template>

src/components/primitives/Checkbox/Checkbox.vue

Lines changed: 21 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,28 @@
1-
<script>
2-
/* eslint no-shadow: ["error", { "allow": ["focus"] }] -- prevent warning 'focus' is already declared in the upper scope */
3-
import { ref, watchEffect } from "vue";
4-
import CheckRadioMixin from "../../../mixins/CheckRadio.js";
1+
<script setup lang="ts">
2+
import type { CheckRadioProps } from "@/types/CheckRadioProps";
3+
import { useTemplateRef } from "vue";
54
6-
export default {
7-
name: "VCheckbox",
8-
props: {
9-
...CheckRadioMixin.props,
10-
indeterminate: Boolean,
11-
trueValue: {
12-
type: [String, Number, Boolean, Function, Object, Array],
13-
default: true,
14-
},
15-
falseValue: {
16-
type: [String, Number, Boolean, Function, Object, Array],
17-
default: false,
18-
},
19-
},
20-
emits: ["update:modelValue"],
21-
setup(props, { emit }) {
22-
const label = ref(null);
23-
const input = ref(null);
24-
const value = ref(props.modelValue);
5+
interface IProps extends CheckRadioProps {
6+
indeterminate?: boolean;
7+
// biome-ignore lint/suspicious/noExplicitAny: allow any type according to docs
8+
trueValue?: any;
9+
// biome-ignore lint/suspicious/noExplicitAny: allow any type according to docs
10+
falseValue?: any;
11+
}
2512
26-
const focus = () => {
27-
input.value.focus();
28-
};
13+
const props = withDefaults(defineProps<IProps>(), {
14+
trueValue: true,
15+
falseValue: false, // todo update doc: set false instead N/A
16+
});
2917
30-
watchEffect(() => {
31-
emit("update:modelValue", value.value);
32-
});
18+
const emit = defineEmits(["update:modelValue"]);
3319
34-
watchEffect(() => {
35-
value.value = props.modelValue;
36-
});
20+
const label = useTemplateRef<HTMLLabelElement>("label");
21+
const input = useTemplateRef<HTMLInputElement>("input");
22+
const value = defineModel();
3723
38-
return { label, input, value, focus };
39-
},
24+
const focus = () => {
25+
input.value?.focus();
4026
};
4127
</script>
4228

@@ -47,7 +33,7 @@ export default {
4733
ref="label"
4834
:disabled="disabled"
4935
@click="focus"
50-
@keydown.prevent.enter="label.click()">
36+
@keydown.prevent.enter="label?.click()">
5137
<input
5238
v-model="value"
5339
:indeterminate.prop="indeterminate"
Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,41 @@
1-
<script>
2-
import { computed } from "vue";
1+
<script setup lang="ts">
2+
// todo add docs to https://vue3.dev/documentation
3+
import { checkBenchieSupport } from "@/utils/functions";
4+
import { computed, onBeforeMount, ref } from "vue";
35
4-
export default {
5-
name: "VContainer",
6-
props: {
7-
type: String,
8-
bg: Object,
9-
},
10-
setup(props) {
11-
const rootClasses = computed(() => {
12-
return [
13-
props.bg
14-
? {
15-
"background-image": `url(${props.bg})`,
16-
"background-size": "cover",
17-
"background-repeat": "no-repeat",
18-
}
19-
: {},
20-
];
21-
});
22-
return { rootClasses };
23-
},
24-
};
6+
const hasBenchieSupport = checkBenchieSupport();
7+
8+
const props = defineProps<{
9+
type?: string;
10+
bg?: string;
11+
}>();
12+
13+
const bgImageUrl = ref(props.bg);
14+
15+
onBeforeMount(async () => {
16+
if (props.bg && hasBenchieSupport) {
17+
bgImageUrl.value = await t(props.bg, $__CDN);
18+
}
19+
});
20+
21+
const rootStyle = computed(() => {
22+
return [
23+
props.bg
24+
? {
25+
"background-image": `url(${bgImageUrl.value})`,
26+
"background-size": "cover",
27+
"background-repeat": "no-repeat",
28+
}
29+
: {},
30+
];
31+
});
2532
</script>
2633

2734
<template>
2835
<div
2936
class="container"
3037
:class="[type]"
31-
:style="rootClasses">
38+
:style="rootStyle">
3239
<slot />
3340
</div>
3441
</template>
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import { mount } from "@vue/test-utils";
2+
import { describe, expect, it, vi } from "vitest";
3+
import Container from "../Container.vue";
4+
5+
vi.mock("@/utils/functions", () => ({
6+
checkBenchieSupport: () => false,
7+
}));
8+
9+
describe("Container", () => {
10+
it("renders with default props", () => {
11+
const wrapper = mount(Container);
12+
expect(wrapper.html({ raw: true })).toContain(
13+
'<div class="container"></div>',
14+
);
15+
});
16+
17+
it("render with type class", () => {
18+
const wrapper = mount(Container, {
19+
props: {
20+
type: "type-class-1 type-class-2",
21+
},
22+
});
23+
expect(wrapper.classes()).length(3);
24+
expect(wrapper.classes()).toContain("container");
25+
expect(wrapper.classes()).toContain("type-class-1");
26+
expect(wrapper.classes()).toContain("type-class-2");
27+
28+
// check order
29+
expect(wrapper.html({ raw: true })).toContain(
30+
'<div class="container type-class-1 type-class-2"></div>',
31+
);
32+
});
33+
34+
it("render with class attribute", () => {
35+
const wrapper = mount(Container, {
36+
attrs: {
37+
class: "custom-class",
38+
},
39+
});
40+
expect(wrapper.classes()).length(2);
41+
expect(wrapper.classes()).toContain("container");
42+
expect(wrapper.classes()).toContain("custom-class");
43+
44+
// check order
45+
expect(wrapper.html({ raw: true })).toContain(
46+
'<div class="container custom-class"></div>',
47+
);
48+
});
49+
50+
it("render with type class and class attribute", () => {
51+
const wrapper = mount(Container, {
52+
props: {
53+
type: "type-class-1 type-class-2",
54+
},
55+
attrs: {
56+
class: "custom-class-1 custom-class-2",
57+
},
58+
});
59+
expect(wrapper.classes()).length(5);
60+
expect(wrapper.classes()).toContain("container");
61+
expect(wrapper.classes()).toContain("type-class-1");
62+
expect(wrapper.classes()).toContain("type-class-2");
63+
expect(wrapper.classes()).toContain("custom-class-1");
64+
expect(wrapper.classes()).toContain("custom-class-2");
65+
66+
// check order
67+
expect(wrapper.html({ raw: true })).toContain(
68+
'<div class="container type-class-1 type-class-2 custom-class-1 custom-class-2"></div>',
69+
);
70+
});
71+
72+
it("render with class attribute", () => {
73+
const wrapper = mount(Container, {
74+
attrs: {
75+
class: "custom-class",
76+
},
77+
});
78+
expect(wrapper.classes()).length(2);
79+
expect(wrapper.classes()).toContain("container");
80+
expect(wrapper.classes()).toContain("custom-class");
81+
});
82+
83+
it("render with type class and class attribute", () => {
84+
const wrapper = mount(Container, {
85+
props: {
86+
type: "type-class-1 type-class-2",
87+
},
88+
attrs: {
89+
class: "custom-class-1 custom-class-2",
90+
},
91+
});
92+
expect(wrapper.classes()).length(5);
93+
expect(wrapper.classes()).toContain("container");
94+
expect(wrapper.classes()).toContain("type-class-1");
95+
expect(wrapper.classes()).toContain("type-class-2");
96+
expect(wrapper.classes()).toContain("custom-class-1");
97+
expect(wrapper.classes()).toContain("custom-class-2");
98+
});
99+
100+
it("render with background image", () => {
101+
const wrapper = mount(Container, {
102+
props: {
103+
bg: "https://example.com/image.jpg",
104+
},
105+
});
106+
const containerEl = wrapper.element;
107+
expect(containerEl.style.backgroundImage).toBe(
108+
"url(https://example.com/image.jpg)",
109+
);
110+
expect(containerEl.style.backgroundSize).toBe("cover");
111+
expect(containerEl.style.backgroundRepeat).toBe("no-repeat");
112+
});
113+
});

src/components/primitives/FileInput/FileInput.vue

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
1-
<script>
2-
export default {
3-
name: "VFile",
1+
<script setup lang="ts">
2+
// todo add docs about this component to https://vue3.dev/documentation
3+
4+
defineOptions({
45
inheritAttrs: false,
5-
props: {
6-
hasName: Boolean,
7-
fullwidth: Boolean,
8-
boxed: Boolean,
9-
size: String,
10-
color: {
11-
type: String,
12-
default: "is-primary",
13-
},
14-
alignment: String,
6+
});
7+
8+
withDefaults(
9+
defineProps<{
10+
hasName?: boolean;
11+
fullwidth?: boolean;
12+
boxed?: boolean;
13+
size?: string;
14+
color?: string;
15+
alignment?: string;
16+
}>(),
17+
{
18+
color: "is-primary",
1519
},
16-
};
20+
);
1721
</script>
1822

1923
<template>

0 commit comments

Comments
 (0)