@kiva/kv-components 3.53.0 → 3.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/package.json +2 -2
- package/tests/fixtures/mockFeedActivityData.js +1 -0
- package/tests/unit/specs/components/KvCommentsAdd.spec.js +23 -1
- package/tests/unit/specs/components/KvCommentsHeartButton.spec.js +32 -0
- package/tests/unit/specs/components/KvCommentsList.spec.js +3 -2
- package/tests/unit/specs/components/KvCommentsListItem.spec.js +11 -8
- package/tests/unit/specs/components/KvCommentsReplyButton.spec.js +23 -0
- package/vue/KvButton.vue +5 -5
- package/vue/KvCommentsAdd.vue +22 -2
- package/vue/KvCommentsHeartButton.vue +74 -0
- package/vue/KvCommentsList.vue +13 -7
- package/vue/KvCommentsListItem.vue +59 -22
- package/vue/KvCommentsReplyButton.vue +49 -0
- package/vue/stories/KvCommentsHeartButton.stories.js +25 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,35 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [3.55.0](https://github.com/kiva/kv-ui-elements/compare/@kiva/kv-components@3.54.0...@kiva/kv-components@3.55.0) (2024-02-29)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* input width ([2489339](https://github.com/kiva/kv-ui-elements/commit/2489339c622ac45739bcc1eefc0a69837f90b841))
|
|
12
|
+
* when to send hide event and test not recognizing vue3 ([c3941a7](https://github.com/kiva/kv-ui-elements/commit/c3941a7a73a439846e6abf16b6dd0b29fa0e36f7))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* adding input for comment reply ([b18edf0](https://github.com/kiva/kv-ui-elements/commit/b18edf0ecd300f622379d160237fd750cf117fa1))
|
|
18
|
+
* update tests and remove handleClick for emits ([fc3c586](https://github.com/kiva/kv-ui-elements/commit/fc3c58667248fb4e44bb0a7558f6c88f97f299b1))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# [3.54.0](https://github.com/kiva/kv-ui-elements/compare/@kiva/kv-components@3.53.0...@kiva/kv-components@3.54.0) (2024-02-28)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Features
|
|
28
|
+
|
|
29
|
+
* kvcommentsheartbutton added for comments stuff ([#346](https://github.com/kiva/kv-ui-elements/issues/346)) ([57303da](https://github.com/kiva/kv-ui-elements/commit/57303daec80222c199556b296d720462265e53ac))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
6
35
|
# [3.53.0](https://github.com/kiva/kv-ui-elements/compare/@kiva/kv-components@3.52.1...@kiva/kv-components@3.53.0) (2024-02-26)
|
|
7
36
|
|
|
8
37
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kiva/kv-components",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.55.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -75,5 +75,5 @@
|
|
|
75
75
|
"optional": true
|
|
76
76
|
}
|
|
77
77
|
},
|
|
78
|
-
"gitHead": "
|
|
78
|
+
"gitHead": "ecd01faa5db8db12e79b243f311a264616a23e7d"
|
|
79
79
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { render } from '@testing-library/vue';
|
|
2
2
|
import userEvent from '@testing-library/user-event';
|
|
3
|
-
import CommentsAdd, { ADD_COMMENT_EVENT } from '../../../../vue/KvCommentsAdd.vue';
|
|
3
|
+
import CommentsAdd, { ADD_COMMENT_EVENT, HIDE_INPUT_EVENT } from '../../../../vue/KvCommentsAdd.vue';
|
|
4
4
|
|
|
5
5
|
const renderCommentsAdd = (props = {}) => {
|
|
6
6
|
return render(CommentsAdd, { props });
|
|
@@ -71,4 +71,26 @@ describe('KvCommentsAdd', () => {
|
|
|
71
71
|
|
|
72
72
|
expect(emitted()[ADD_COMMENT_EVENT]).toBe(undefined);
|
|
73
73
|
});
|
|
74
|
+
|
|
75
|
+
it('should emit close event when it replies a comment', async () => {
|
|
76
|
+
const { getByRole, emitted, getByPlaceholderText } = renderCommentsAdd({ isReply: true });
|
|
77
|
+
const textInput = getByPlaceholderText('Add a comment to this loan...');
|
|
78
|
+
const commentButton = getByRole('button', { name: 'Comment' });
|
|
79
|
+
const TEST_INPUT = 'test test';
|
|
80
|
+
|
|
81
|
+
await userEvent.type(textInput, TEST_INPUT);
|
|
82
|
+
|
|
83
|
+
await userEvent.click(commentButton);
|
|
84
|
+
|
|
85
|
+
expect(emitted()[HIDE_INPUT_EVENT]).toEqual([[]]);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should emit close event when it clicks cancel on reply', async () => {
|
|
89
|
+
const { getByRole, emitted } = renderCommentsAdd({ isReply: true });
|
|
90
|
+
const cancelButton = getByRole('button', { name: 'Cancel' });
|
|
91
|
+
|
|
92
|
+
await userEvent.click(cancelButton);
|
|
93
|
+
|
|
94
|
+
expect(emitted()[HIDE_INPUT_EVENT]).toEqual([[]]);
|
|
95
|
+
});
|
|
74
96
|
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { render } from '@testing-library/vue';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import KvCommentsHeartButton from '../../../../vue/KvCommentsHeartButton.vue';
|
|
4
|
+
|
|
5
|
+
const CLICK_EVENT = 'click';
|
|
6
|
+
|
|
7
|
+
describe('KvCommentsListItem', () => {
|
|
8
|
+
it('should render defaults', () => {
|
|
9
|
+
const { getByRole } = render(KvCommentsHeartButton);
|
|
10
|
+
const likeButton = getByRole('button', { name: 'Like' });
|
|
11
|
+
|
|
12
|
+
expect(likeButton).toBeDefined();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should emit true value when clicked as not liked', async () => {
|
|
16
|
+
const { getByRole, emitted } = render(KvCommentsHeartButton);
|
|
17
|
+
const likeButton = getByRole('button', { name: 'Like' });
|
|
18
|
+
|
|
19
|
+
await userEvent.click(likeButton);
|
|
20
|
+
|
|
21
|
+
expect(emitted()[CLICK_EVENT]).toEqual([[true]]);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should emit false value when clicked as liked', async () => {
|
|
25
|
+
const { getByRole, emitted } = render(KvCommentsHeartButton, { props: { isLiked: true } });
|
|
26
|
+
const likeButton = getByRole('button', { name: 'Like' });
|
|
27
|
+
|
|
28
|
+
await userEvent.click(likeButton);
|
|
29
|
+
|
|
30
|
+
expect(emitted()[CLICK_EVENT]).toEqual([[false]]);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -19,7 +19,7 @@ describe('KvCommentsList', () => {
|
|
|
19
19
|
});
|
|
20
20
|
|
|
21
21
|
it('should emit comment events', async () => {
|
|
22
|
-
const { getAllByRole, emitted } = renderList({ comments });
|
|
22
|
+
const { getAllByRole, getByRole, emitted } = renderList({ comments });
|
|
23
23
|
const replyButton = getAllByRole('button', { name: 'Reply' })[0];
|
|
24
24
|
const likeButton = getAllByRole('button', { name: 'Like' })[0];
|
|
25
25
|
const firstComment = comments.comment[0];
|
|
@@ -32,8 +32,9 @@ describe('KvCommentsList', () => {
|
|
|
32
32
|
|
|
33
33
|
await userEvent.click(replyButton);
|
|
34
34
|
expect(emitted()[REPLY_COMMENT_EVENT]).toEqual([[{ ...TEST_OBJ, reaction: REPLY_COMMENT_EVENT }]]);
|
|
35
|
+
getByRole('button', { name: 'Comment' });
|
|
35
36
|
|
|
36
37
|
await userEvent.click(likeButton);
|
|
37
|
-
expect(emitted()[LIKE_COMMENT_EVENT]).toEqual([[{ ...TEST_OBJ, reaction: LIKE_COMMENT_EVENT }]]);
|
|
38
|
+
expect(emitted()[LIKE_COMMENT_EVENT]).toEqual([[{ ...TEST_OBJ, reaction: LIKE_COMMENT_EVENT, value: true }]]);
|
|
38
39
|
});
|
|
39
40
|
});
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { render } from '@testing-library/vue';
|
|
2
2
|
import userEvent from '@testing-library/user-event';
|
|
3
|
-
import KvCommentsListItem from '../../../../vue/KvCommentsListItem.vue';
|
|
3
|
+
import KvCommentsListItem, { LIKE_COMMENT_EVENT } from '../../../../vue/KvCommentsListItem.vue';
|
|
4
4
|
import activityFeed from '../../../fixtures/mockFeedActivityData';
|
|
5
5
|
|
|
6
6
|
const comment = activityFeed.results[0].latest_reactions.comment[0];
|
|
7
7
|
|
|
8
|
-
const handleClick = jest.fn();
|
|
9
|
-
|
|
10
8
|
const renderComment = (props = {}) => {
|
|
11
9
|
return render(KvCommentsListItem, { props });
|
|
12
10
|
};
|
|
@@ -24,20 +22,25 @@ describe('KvCommentsListItem', () => {
|
|
|
24
22
|
});
|
|
25
23
|
|
|
26
24
|
it('should handle like button click', async () => {
|
|
27
|
-
const { getByRole } = renderComment({ comment
|
|
25
|
+
const { getByRole, emitted } = renderComment({ comment });
|
|
28
26
|
const likeButton = getByRole('button', { name: 'Like' });
|
|
29
27
|
|
|
30
28
|
await userEvent.click(likeButton);
|
|
31
29
|
|
|
32
|
-
expect(
|
|
30
|
+
expect(emitted()[LIKE_COMMENT_EVENT]).toEqual([[{
|
|
31
|
+
id: comment.id,
|
|
32
|
+
isChild: true,
|
|
33
|
+
reaction: LIKE_COMMENT_EVENT,
|
|
34
|
+
userId: comment.user_id,
|
|
35
|
+
value: true,
|
|
36
|
+
}]]);
|
|
33
37
|
});
|
|
34
38
|
|
|
35
39
|
it('should handle reply button click', async () => {
|
|
36
|
-
const { getByRole } = renderComment({ comment
|
|
40
|
+
const { getByRole } = renderComment({ comment });
|
|
37
41
|
const replyButton = getByRole('button', { name: 'Reply' });
|
|
38
42
|
|
|
39
43
|
await userEvent.click(replyButton);
|
|
40
|
-
|
|
41
|
-
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
44
|
+
getByRole('button', { name: 'Comment' });
|
|
42
45
|
});
|
|
43
46
|
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { render } from '@testing-library/vue';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import KvCommentsReplyButton from '../../../../vue/KvCommentsReplyButton.vue';
|
|
4
|
+
|
|
5
|
+
const CLICK_EVENT = 'click';
|
|
6
|
+
|
|
7
|
+
describe('KvCommentsReplyButton', () => {
|
|
8
|
+
it('should render defaults', () => {
|
|
9
|
+
const { getByRole } = render(KvCommentsReplyButton);
|
|
10
|
+
const replyButton = getByRole('button', { name: 'Reply' });
|
|
11
|
+
|
|
12
|
+
expect(replyButton).toBeDefined();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should emit true value when clicked as not liked', async () => {
|
|
16
|
+
const { getByRole, emitted } = render(KvCommentsReplyButton);
|
|
17
|
+
const likeButton = getByRole('button', { name: 'Reply' });
|
|
18
|
+
|
|
19
|
+
await userEvent.click(likeButton);
|
|
20
|
+
|
|
21
|
+
expect(emitted()[CLICK_EVENT]).toEqual([[]]);
|
|
22
|
+
});
|
|
23
|
+
});
|
package/vue/KvButton.vue
CHANGED
|
@@ -53,14 +53,14 @@ export default {
|
|
|
53
53
|
props: {
|
|
54
54
|
/**
|
|
55
55
|
* Use if linking to a Vue route
|
|
56
|
-
|
|
56
|
+
*/
|
|
57
57
|
to: {
|
|
58
58
|
type: [String, Object],
|
|
59
59
|
default: null,
|
|
60
60
|
},
|
|
61
61
|
/**
|
|
62
62
|
* Use if linking to an external link or old-stack page
|
|
63
|
-
|
|
63
|
+
*/
|
|
64
64
|
href: {
|
|
65
65
|
type: String,
|
|
66
66
|
default: null,
|
|
@@ -68,7 +68,7 @@ export default {
|
|
|
68
68
|
/**
|
|
69
69
|
* The behavior of the button when used in an HTML form.
|
|
70
70
|
* `button (default), submit, reset`
|
|
71
|
-
|
|
71
|
+
*/
|
|
72
72
|
type: {
|
|
73
73
|
type: String,
|
|
74
74
|
default: 'button',
|
|
@@ -79,7 +79,7 @@ export default {
|
|
|
79
79
|
/**
|
|
80
80
|
* Appearance of the button
|
|
81
81
|
* `primary (default), secondary, danger, link, ghost`
|
|
82
|
-
|
|
82
|
+
*/
|
|
83
83
|
variant: {
|
|
84
84
|
type: String,
|
|
85
85
|
default: 'primary',
|
|
@@ -90,7 +90,7 @@ export default {
|
|
|
90
90
|
/**
|
|
91
91
|
* State of the button
|
|
92
92
|
* `'' (default), active, disabled, loading`
|
|
93
|
-
|
|
93
|
+
*/
|
|
94
94
|
state: {
|
|
95
95
|
type: String,
|
|
96
96
|
default: '',
|
package/vue/KvCommentsAdd.vue
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
</div>
|
|
23
23
|
<kv-text-input
|
|
24
24
|
:id="ADD_COMMENT_ID"
|
|
25
|
+
ref="input"
|
|
25
26
|
v-model="addCommentValue"
|
|
26
27
|
placeholder="Add a comment to this loan..."
|
|
27
28
|
class="data-hj-suppress tw-grow"
|
|
@@ -53,6 +54,7 @@ import KvTextInput from './KvTextInput.vue';
|
|
|
53
54
|
|
|
54
55
|
export const ADD_COMMENT_ID = 'add-comment-value';
|
|
55
56
|
export const ADD_COMMENT_EVENT = 'add-comment';
|
|
57
|
+
export const HIDE_INPUT_EVENT = 'hide-input';
|
|
56
58
|
|
|
57
59
|
export default {
|
|
58
60
|
components: {
|
|
@@ -74,27 +76,45 @@ export default {
|
|
|
74
76
|
type: String,
|
|
75
77
|
default: '',
|
|
76
78
|
},
|
|
79
|
+
/**
|
|
80
|
+
* Whether or not the comment is a reply
|
|
81
|
+
*/
|
|
82
|
+
isReply: {
|
|
83
|
+
type: Boolean,
|
|
84
|
+
default: false,
|
|
85
|
+
},
|
|
77
86
|
},
|
|
78
|
-
emits: [ADD_COMMENT_EVENT],
|
|
79
|
-
setup(
|
|
87
|
+
emits: [ADD_COMMENT_EVENT, HIDE_INPUT_EVENT],
|
|
88
|
+
setup(props, { emit }) {
|
|
80
89
|
const addCommentValue = ref('');
|
|
90
|
+
const input = ref(null);
|
|
81
91
|
|
|
82
92
|
const commentButtonState = computed(() => (addCommentValue.value ? '' : 'disabled'));
|
|
83
93
|
|
|
84
94
|
const cancel = () => {
|
|
85
95
|
addCommentValue.value = '';
|
|
96
|
+
if (props.isReply) {
|
|
97
|
+
emit(HIDE_INPUT_EVENT);
|
|
98
|
+
}
|
|
86
99
|
};
|
|
87
100
|
|
|
88
101
|
const comment = () => {
|
|
89
102
|
emit(ADD_COMMENT_EVENT, addCommentValue.value);
|
|
103
|
+
addCommentValue.value = '';
|
|
104
|
+
if (props.isReply) {
|
|
105
|
+
emit(HIDE_INPUT_EVENT);
|
|
106
|
+
}
|
|
90
107
|
};
|
|
91
108
|
|
|
109
|
+
const focus = () => input.value.focus();
|
|
110
|
+
|
|
92
111
|
return {
|
|
93
112
|
ADD_COMMENT_ID,
|
|
94
113
|
addCommentValue,
|
|
95
114
|
commentButtonState,
|
|
96
115
|
cancel,
|
|
97
116
|
comment,
|
|
117
|
+
focus,
|
|
98
118
|
};
|
|
99
119
|
},
|
|
100
120
|
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<button
|
|
3
|
+
aria-label="Like"
|
|
4
|
+
@click="changeState"
|
|
5
|
+
>
|
|
6
|
+
<kv-material-icon
|
|
7
|
+
:icon="icon"
|
|
8
|
+
:class="classState"
|
|
9
|
+
/>
|
|
10
|
+
</button>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script>
|
|
14
|
+
import { computed, toRefs } from 'vue-demi';
|
|
15
|
+
import { mdiHeart, mdiHeartOutline } from '@mdi/js';
|
|
16
|
+
import KvMaterialIcon from './KvMaterialIcon.vue';
|
|
17
|
+
|
|
18
|
+
export default {
|
|
19
|
+
name: 'KvCommentsHeartButton',
|
|
20
|
+
components: {
|
|
21
|
+
KvMaterialIcon,
|
|
22
|
+
},
|
|
23
|
+
props: {
|
|
24
|
+
/**
|
|
25
|
+
* Use if small icon is needed
|
|
26
|
+
*/
|
|
27
|
+
isSmall: {
|
|
28
|
+
type: Boolean,
|
|
29
|
+
default: false,
|
|
30
|
+
},
|
|
31
|
+
/**
|
|
32
|
+
* Use if icon is liked
|
|
33
|
+
*/
|
|
34
|
+
isLiked: {
|
|
35
|
+
type: Boolean,
|
|
36
|
+
default: false,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
emits: [
|
|
40
|
+
'click',
|
|
41
|
+
],
|
|
42
|
+
setup(props, { emit }) {
|
|
43
|
+
const {
|
|
44
|
+
isSmall,
|
|
45
|
+
isLiked,
|
|
46
|
+
} = toRefs(props);
|
|
47
|
+
|
|
48
|
+
const icon = computed(() => (isLiked.value ? mdiHeart : mdiHeartOutline));
|
|
49
|
+
|
|
50
|
+
const classState = computed(() => {
|
|
51
|
+
let className = isSmall.value ? 'tw-w-2.5 tw-h-2.5' : 'tw-w-3 tw-h-3';
|
|
52
|
+
className += isLiked.value ? ' filled' : '';
|
|
53
|
+
|
|
54
|
+
return className;
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const changeState = () => {
|
|
58
|
+
emit('click', !isLiked.value);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
icon,
|
|
63
|
+
classState,
|
|
64
|
+
changeState,
|
|
65
|
+
};
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
</script>
|
|
69
|
+
|
|
70
|
+
<style scoped>
|
|
71
|
+
.filled >>> svg {
|
|
72
|
+
fill: #F60059;
|
|
73
|
+
}
|
|
74
|
+
</style>
|
package/vue/KvCommentsList.vue
CHANGED
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
:key="comment.id"
|
|
7
7
|
:nest-level="1"
|
|
8
8
|
:comment="comment"
|
|
9
|
-
:
|
|
9
|
+
:is-liked="comment.is_liked"
|
|
10
|
+
@[REPLY_COMMENT_EVENT]="handleClick"
|
|
11
|
+
@[LIKE_COMMENT_EVENT]="handleClick"
|
|
10
12
|
/>
|
|
11
13
|
</div>
|
|
12
14
|
</template>
|
|
@@ -26,12 +28,16 @@ export default {
|
|
|
26
28
|
default: () => {},
|
|
27
29
|
},
|
|
28
30
|
},
|
|
29
|
-
|
|
30
|
-
handleClick(payload) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
setup(_props, { emit }) {
|
|
32
|
+
const handleClick = (payload) => {
|
|
33
|
+
emit(payload.reaction, { ...payload });
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
handleClick,
|
|
38
|
+
REPLY_COMMENT_EVENT,
|
|
39
|
+
LIKE_COMMENT_EVENT,
|
|
40
|
+
};
|
|
35
41
|
},
|
|
36
42
|
};
|
|
37
43
|
</script>
|
|
@@ -21,21 +21,30 @@
|
|
|
21
21
|
{{ text }}
|
|
22
22
|
</p>
|
|
23
23
|
</div>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
class="tw-font-medium"
|
|
28
|
-
@click="onClick(REPLY_COMMENT_EVENT)"
|
|
24
|
+
<div
|
|
25
|
+
v-if="nestLevel < 3"
|
|
26
|
+
class="tw-flex tw-items-center tw-gap-x-2"
|
|
29
27
|
>
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
28
|
+
<kv-comments-heart-button
|
|
29
|
+
:is-small="true"
|
|
30
|
+
:is-liked="isLiked"
|
|
31
|
+
@click="onClick(LIKE_COMMENT_EVENT, $event)"
|
|
32
|
+
/>
|
|
33
|
+
<kv-comments-reply-button
|
|
34
|
+
@click="onClick(REPLY_COMMENT_EVENT, $event)"
|
|
35
|
+
/>
|
|
36
|
+
</div>
|
|
37
|
+
<div
|
|
38
|
+
v-if="showInput"
|
|
39
|
+
class="tw-w-full"
|
|
36
40
|
>
|
|
37
|
-
|
|
38
|
-
|
|
41
|
+
<kv-comments-add
|
|
42
|
+
ref="commentsAddRef"
|
|
43
|
+
user-mention
|
|
44
|
+
@add-comment="onClick(REPLY_COMMENT_EVENT, $event)"
|
|
45
|
+
@hide-input="hideInput"
|
|
46
|
+
/>
|
|
47
|
+
</div>
|
|
39
48
|
<div
|
|
40
49
|
v-if="latestChildren"
|
|
41
50
|
class="tw-my-1"
|
|
@@ -47,6 +56,7 @@
|
|
|
47
56
|
>
|
|
48
57
|
<kv-comments-list-item
|
|
49
58
|
:comment="nested_comment"
|
|
59
|
+
:is-liked="nested_comment.is_liked"
|
|
50
60
|
:nest-level="nestLevel + 1"
|
|
51
61
|
:handle-click="handleClick"
|
|
52
62
|
/>
|
|
@@ -56,14 +66,21 @@
|
|
|
56
66
|
</template>
|
|
57
67
|
|
|
58
68
|
<script>
|
|
59
|
-
import
|
|
69
|
+
import { ref, nextTick } from 'vue-demi';
|
|
70
|
+
import KvCommentsReplyButton from './KvCommentsReplyButton.vue';
|
|
71
|
+
import KvCommentsHeartButton from './KvCommentsHeartButton.vue';
|
|
72
|
+
import KvCommentsAdd from './KvCommentsAdd.vue';
|
|
60
73
|
|
|
61
74
|
export const REPLY_COMMENT_EVENT = 'reply-comment';
|
|
62
75
|
export const LIKE_COMMENT_EVENT = 'like-comment';
|
|
63
76
|
|
|
64
77
|
export default {
|
|
65
78
|
name: 'KvCommentsListItem',
|
|
66
|
-
components: {
|
|
79
|
+
components: {
|
|
80
|
+
KvCommentsReplyButton,
|
|
81
|
+
KvCommentsHeartButton,
|
|
82
|
+
KvCommentsAdd,
|
|
83
|
+
},
|
|
67
84
|
props: {
|
|
68
85
|
/**
|
|
69
86
|
* Activity comment
|
|
@@ -80,24 +97,44 @@ export default {
|
|
|
80
97
|
default: 0,
|
|
81
98
|
},
|
|
82
99
|
/**
|
|
83
|
-
*
|
|
100
|
+
* Comment is liked by current user
|
|
84
101
|
*/
|
|
85
|
-
|
|
86
|
-
type:
|
|
87
|
-
default:
|
|
102
|
+
isLiked: {
|
|
103
|
+
type: Boolean,
|
|
104
|
+
default: false,
|
|
88
105
|
},
|
|
89
106
|
},
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
107
|
+
emits: [
|
|
108
|
+
REPLY_COMMENT_EVENT,
|
|
109
|
+
LIKE_COMMENT_EVENT,
|
|
110
|
+
],
|
|
111
|
+
setup(props, { emit }) {
|
|
112
|
+
const showInput = ref(false);
|
|
113
|
+
const commentsAddRef = ref(null);
|
|
114
|
+
|
|
115
|
+
const onClick = (reaction, value) => {
|
|
116
|
+
if (reaction === REPLY_COMMENT_EVENT) {
|
|
117
|
+
showInput.value = true;
|
|
118
|
+
nextTick(() => {
|
|
119
|
+
commentsAddRef.value.$refs.input.focus();
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
emit(reaction, {
|
|
93
124
|
reaction,
|
|
94
125
|
id: props.comment?.id ?? null,
|
|
95
126
|
userId: props.comment?.user_id ?? null,
|
|
96
127
|
isChild: true,
|
|
128
|
+
value,
|
|
97
129
|
});
|
|
98
130
|
};
|
|
99
131
|
|
|
132
|
+
const hideInput = () => { showInput.value = false; };
|
|
133
|
+
|
|
100
134
|
return {
|
|
135
|
+
hideInput,
|
|
136
|
+
showInput,
|
|
137
|
+
commentsAddRef,
|
|
101
138
|
onClick,
|
|
102
139
|
REPLY_COMMENT_EVENT,
|
|
103
140
|
LIKE_COMMENT_EVENT,
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<!-- eslint-disable max-len -->
|
|
2
|
+
<template>
|
|
3
|
+
<button
|
|
4
|
+
ref="replyButton"
|
|
5
|
+
aria-label="Reply"
|
|
6
|
+
class="tw-font-medium tw-flex tw-items-center tw-gap-x-0.5"
|
|
7
|
+
@click="handleClick"
|
|
8
|
+
>
|
|
9
|
+
<svg
|
|
10
|
+
width="14"
|
|
11
|
+
height="11"
|
|
12
|
+
viewBox="0 0 14 11"
|
|
13
|
+
fill="none"
|
|
14
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
15
|
+
>
|
|
16
|
+
<path
|
|
17
|
+
d="M2.87502 5.12499L4.91669 7.16666C5.06946 7.31943 5.14585 7.49652 5.14585 7.69791C5.14585 7.8993 5.06946 8.0797 4.91669 8.23911C4.76391 8.38525 4.58683 8.45832 4.38544 8.45832C4.18405 8.45832 4.00696 8.38193 3.85419 8.22916L0.520854 4.89582C0.368076 4.7481 0.291687 4.57575 0.291687 4.37878C0.291687 4.18181 0.368076 4.00693 0.520854 3.85416L3.85419 0.520823C4.00696 0.368045 4.18521 0.291656 4.38892 0.291656C4.59261 0.291656 4.76853 0.368045 4.91669 0.520823C5.06946 0.668976 5.14585 0.8449 5.14585 1.04859C5.14585 1.2523 5.06946 1.43055 4.91669 1.58332L2.87502 3.62499H10C11.1067 3.62499 12.05 4.01499 12.83 4.79499C13.61 5.57499 14 6.51832 14 7.62499V9.87499C14 10.0875 13.9286 10.2656 13.7857 10.4094C13.6427 10.5531 13.4657 10.625 13.2544 10.625C13.0432 10.625 12.8646 10.5531 12.7188 10.4094C12.5729 10.2656 12.5 10.0875 12.5 9.87499V7.62499C12.5 6.93055 12.257 6.34027 11.7709 5.85416C11.2847 5.36805 10.6945 5.12499 10 5.12499H2.87502Z"
|
|
18
|
+
fill="#1C1B1F"
|
|
19
|
+
/>
|
|
20
|
+
</svg>
|
|
21
|
+
|
|
22
|
+
<span>
|
|
23
|
+
Reply
|
|
24
|
+
</span>
|
|
25
|
+
</button>
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<script>
|
|
29
|
+
import { ref } from 'vue-demi';
|
|
30
|
+
|
|
31
|
+
export default {
|
|
32
|
+
name: 'KvCommentsReplyButton',
|
|
33
|
+
emits: [
|
|
34
|
+
'click',
|
|
35
|
+
],
|
|
36
|
+
setup(_props, { emit }) {
|
|
37
|
+
const replyButton = ref(null);
|
|
38
|
+
|
|
39
|
+
const handleClick = () => {
|
|
40
|
+
emit('click');
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
replyButton,
|
|
45
|
+
handleClick,
|
|
46
|
+
};
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
</script>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import KvCommentsHeartButton from '../KvCommentsHeartButton.vue';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
title: 'KvCommentsHeartButton',
|
|
5
|
+
component: KvCommentsHeartButton,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const story = (args) => {
|
|
9
|
+
const template = (templateArgs, { argTypes }) => ({
|
|
10
|
+
props: Object.keys(argTypes),
|
|
11
|
+
components: { KvCommentsHeartButton },
|
|
12
|
+
setup() { return { args: templateArgs }; },
|
|
13
|
+
template: `
|
|
14
|
+
<KvCommentsHeartButton v-bind="args" />
|
|
15
|
+
`,
|
|
16
|
+
});
|
|
17
|
+
template.args = args;
|
|
18
|
+
return template;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const Default = story({});
|
|
22
|
+
|
|
23
|
+
export const IsSmall = story({ isSmall: true });
|
|
24
|
+
|
|
25
|
+
export const IsLiked = story({ isLiked: true });
|