Skip to content

Commit 808d34e

Browse files
committed
refactor: Add tests cases for DPTrigger component, inject values from root instead duplicating injection
1 parent 47f8d06 commit 808d34e

File tree

5 files changed

+130
-52
lines changed

5 files changed

+130
-52
lines changed

src/packages/components/DpInput/useInput.ts

+3-5
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,28 @@ import { isAfter, isDate, isValid, parse, set } from 'date-fns';
33
import { useContext } from '@packages/composables/useContext.ts';
44
import { useDefaults } from '@packages/composables/useDefaults.ts';
55
import { isValidDate } from '@packages/utils/date.ts';
6-
import { DpTriggerKey } from '@packages/components/DpTrigger';
76
import type { MaybeValue } from '@packages/types';
87
import type { DpInputProps, DpInputEmits } from '@packages/components/DpInput';
8+
import { DpRootKey } from '@packages/components/DpRoot';
99

1010
export const useInput = (props: DpInputProps, emit: DpInputEmits) => {
1111
const {
12-
inputRef,
1312
inputValue,
1413
isMenuOpen,
1514
rangeEnabled,
1615
multiDatesEnabled,
1716
openMenu,
1817
closeMenu,
19-
// setEmptyDate,
20-
// mode,
2118
props: injectedProps,
22-
} = useContext(DpTriggerKey, 'DatePickerTrigger');
19+
} = useContext(DpRootKey);
2320

2421
const { getDefaultTextInputOptions, getDefaultPattern, getDefaultStartTime } = useDefaults();
2522

2623
const textInputOptions = computed(() => getDefaultTextInputOptions(props.textInput));
2724

2825
const parsedDate = ref<MaybeValue<Date> | MaybeValue<Date>[]>(null);
2926
const textPasted = ref(false);
27+
const inputRef = ref<HTMLElement | null>(null);
3028

3129
const propagateEvent = (emitFn: () => void) => {
3230
if (props.propagateEvents) {
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,34 @@
1-
import { type EmitFn, h, type SetupContext } from 'vue';
1+
import { defineComponent, type EmitFn, type ExtractPropTypes, h, type PropType } from 'vue';
22

33
import { emitDef } from '@packages/utils/generic.ts';
44
import { useTrigger } from '@packages/components/DpTrigger';
55

6+
const props = {
7+
as: { type: String as PropType<string>, default: 'div' },
8+
};
9+
610
const emits = {
711
click: emitDef<MouseEvent>,
812
};
913

1014
export type DpTriggerEmits = EmitFn<typeof emits>;
15+
export type DpTriggerProps = ExtractPropTypes<typeof props>;
1116

12-
export const DpTrigger = {
17+
export const DpTrigger = defineComponent({
18+
props,
1319
emits,
14-
setup(props: any, { slots, emit, attrs }: SetupContext) {
20+
setup(props: DpTriggerProps, { slots, emit, attrs }) {
1521
const { triggerRef, clearValue, inputValue, openMenu } = useTrigger();
1622

1723
const onClick = (ev: MouseEvent) => {
1824
emit('click', ev);
1925
openMenu();
2026
};
2127

28+
// todo - needs to exposed close, toggle?
2229
return () =>
23-
h('div', { ...attrs, ref: triggerRef, onClick }, [
24-
slots.default?.({ clearValue, inputValue: inputValue.value, openMenu }),
30+
h(props.as, { ...attrs, ref: triggerRef, onClick }, [
31+
slots.default?.({ clearValue, value: inputValue.value, openMenu }),
2532
]);
2633
},
27-
};
34+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { describe, expect, it, vi, afterEach } from 'vitest';
2+
import { mount } from '@vue/test-utils';
3+
import { DpTrigger } from '@packages/components/DpTrigger';
4+
import { ref } from 'vue';
5+
import { DpRootKey } from '@packages/components/DpRoot';
6+
7+
const useRootContext = () => {
8+
const triggerRef = ref(null);
9+
const inputValue = ref('value-text');
10+
const openMenu = () => {
11+
return true;
12+
};
13+
14+
const clearValue = () => {
15+
inputValue.value = '';
16+
};
17+
18+
return {
19+
triggerRef,
20+
inputValue,
21+
openMenu,
22+
clearValue,
23+
};
24+
};
25+
26+
const getContext = () => {
27+
return {
28+
provide: {
29+
[DpRootKey]: useRootContext(),
30+
},
31+
};
32+
};
33+
34+
describe('DpTrigger', () => {
35+
afterEach(() => {
36+
vi.restoreAllMocks();
37+
});
38+
39+
it('Should render component', () => {
40+
const wrapper = mount(DpTrigger, { global: getContext() });
41+
42+
expect(wrapper.html()).toContain('<div>');
43+
});
44+
45+
it('Should render component as custom HTML tag', () => {
46+
const wrapper = mount(DpTrigger, { global: getContext(), props: { as: 'section' } });
47+
48+
expect(wrapper.html()).toContain('<section>');
49+
});
50+
51+
it('Should fail to mount without root context', () => {
52+
expect(() => mount(DpTrigger)).toThrowError();
53+
});
54+
55+
it('Should render value', () => {
56+
const wrapper = mount(DpTrigger, {
57+
global: getContext(),
58+
slots: {
59+
default: `
60+
<template #default="{value}">
61+
<button type="button" data-test-id="value">{{value}}</button>
62+
</template>
63+
`,
64+
},
65+
});
66+
67+
const valueEl = wrapper.find(`[data-test-id="value"]`);
68+
69+
expect(valueEl.text()).toEqual('value-text');
70+
});
71+
72+
it('Should trigger open menu function', async () => {
73+
const context = getContext();
74+
const spy = vi.spyOn(context.provide[DpRootKey], 'openMenu');
75+
76+
const wrapper = mount(DpTrigger, {
77+
global: context,
78+
slots: {
79+
default: `
80+
<template #default="{value}">
81+
<button type="button" data-test-id="value">{{value}}</button>
82+
</template>
83+
`,
84+
},
85+
});
86+
87+
await wrapper.find(`[data-test-id="value"]`).trigger('click');
88+
const emits = wrapper.emitted();
89+
90+
expect(emits).toHaveProperty('click');
91+
expect(spy).toHaveBeenCalledTimes(1);
92+
});
93+
94+
it('Should update render value based on value change', async () => {
95+
const wrapper = mount(DpTrigger, {
96+
global: getContext(),
97+
slots: {
98+
default: `
99+
<template #default="{value, clearValue}">
100+
<button type="button" data-test-id="value" @click.stop="clearValue">{{value}}</button>
101+
</template>
102+
`,
103+
},
104+
});
105+
await wrapper.find(`[data-test-id="value"]`).trigger('click');
106+
const valueEl = wrapper.find(`[data-test-id="value"]`);
107+
108+
expect(valueEl.text()).toEqual('');
109+
});
110+
});
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
export { DpTrigger } from './DpTrigger.ts';
2-
export { useTrigger, DpTriggerKey } from './useTrigger.ts';
2+
export { useTrigger } from './useTrigger.ts';
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,8 @@
1-
import { type ComputedRef, type InjectionKey, provide, type Ref, ref } from 'vue';
21
import { useContext } from '@packages/composables/useContext.ts';
3-
import { type DpRootProps, DpRootKey } from '@packages/components/DpRoot';
4-
5-
export interface TriggerState {
6-
inputRef: Ref<HTMLElement | null>;
7-
inputValue: Ref<string>;
8-
isMenuOpen: Ref<boolean>;
9-
rangeEnabled: ComputedRef<boolean>;
10-
multiDatesEnabled: ComputedRef<boolean>;
11-
openMenu: () => void;
12-
closeMenu: () => void;
13-
clearValue: () => void;
14-
props: DpRootProps;
15-
}
16-
17-
export const DpTriggerKey = Symbol('DpTriggerKey') as InjectionKey<TriggerState>;
2+
import { DpRootKey } from '@packages/components/DpRoot';
183

194
export const useTrigger = () => {
20-
const {
21-
triggerRef,
22-
props,
23-
inputValue,
24-
isMenuOpen,
25-
rangeEnabled,
26-
multiDatesEnabled,
27-
openMenu,
28-
clearValue,
29-
closeMenu,
30-
} = useContext(DpRootKey);
31-
32-
provide(DpTriggerKey, {
33-
inputRef: ref(null),
34-
rangeEnabled,
35-
multiDatesEnabled,
36-
inputValue,
37-
isMenuOpen,
38-
openMenu,
39-
closeMenu,
40-
clearValue,
41-
props,
42-
});
5+
const { triggerRef, inputValue, openMenu, clearValue } = useContext(DpRootKey);
436

44-
return { triggerRef, injectedProps: props, inputValue, clearValue, openMenu };
7+
return { triggerRef, inputValue, clearValue, openMenu };
458
};

0 commit comments

Comments
 (0)