@kiva/kv-components 3.50.0 → 3.52.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 +27 -0
- package/package.json +2 -2
- package/tests/unit/specs/components/KvCommentsAdd.spec.js +74 -0
- package/tests/unit/specs/components/KvCommentsContainer.spec.js +28 -0
- package/vue/KvCommentsAdd.vue +120 -0
- package/vue/KvCommentsContainer.vue +44 -0
- package/vue/stories/KvCommentsAdd.stories.js +32 -0
- package/vue/stories/KvCommentsContainer.stories.js +23 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,33 @@
|
|
|
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.52.0](https://github.com/kiva/kv-ui-elements/compare/@kiva/kv-components@3.51.0...@kiva/kv-components@3.52.0) (2024-01-31)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* added missing suppression classes ([5d04867](https://github.com/kiva/kv-ui-elements/commit/5d048672f078d2d229372e37b896c9be025457d9))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
* add comments add component ([1bf5fc3](https://github.com/kiva/kv-ui-elements/commit/1bf5fc388fee61a63f49775a15b50728c140a569))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# [3.51.0](https://github.com/kiva/kv-ui-elements/compare/@kiva/kv-components@3.50.0...@kiva/kv-components@3.51.0) (2024-01-25)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Features
|
|
26
|
+
|
|
27
|
+
* add basic comments container for future comment work ([4f1b3ab](https://github.com/kiva/kv-ui-elements/commit/4f1b3aba7f78ead3cf53a955bbb1b1243c4b2cda))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
6
33
|
# [3.50.0](https://github.com/kiva/kv-ui-elements/compare/@kiva/kv-components@3.49.1...@kiva/kv-components@3.50.0) (2024-01-19)
|
|
7
34
|
|
|
8
35
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kiva/kv-components",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.52.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": "185baa28a3a0e9616ca3ef4cf404b9074d19b373"
|
|
79
79
|
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { render } from '@testing-library/vue';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import CommentsAdd, { ADD_COMMENT_EVENT } from '../../../../vue/KvCommentsAdd.vue';
|
|
4
|
+
|
|
5
|
+
const renderCommentsAdd = (props = {}) => {
|
|
6
|
+
return render(CommentsAdd, { props });
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
describe('KvCommentsAdd', () => {
|
|
10
|
+
it('should render defaults', () => {
|
|
11
|
+
const { getByPlaceholderText, getByRole } = renderCommentsAdd();
|
|
12
|
+
getByPlaceholderText('Add a comment to this loan...');
|
|
13
|
+
getByRole('button', { name: 'Cancel' });
|
|
14
|
+
getByRole('button', { name: 'Comment' });
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should render user info', () => {
|
|
18
|
+
const { getByAltText, getByText } = renderCommentsAdd({
|
|
19
|
+
userImageUrl: 'test.com',
|
|
20
|
+
userDisplayName: 'User',
|
|
21
|
+
});
|
|
22
|
+
getByAltText('Lender');
|
|
23
|
+
getByText('User');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should enable comment button when text entered', async () => {
|
|
27
|
+
const { getByPlaceholderText, getByRole } = renderCommentsAdd();
|
|
28
|
+
const textInput = getByPlaceholderText('Add a comment to this loan...');
|
|
29
|
+
const commentButton = getByRole('button', { name: 'Comment' });
|
|
30
|
+
|
|
31
|
+
expect(commentButton.disabled).toBeTruthy();
|
|
32
|
+
|
|
33
|
+
await userEvent.type(textInput, 'test');
|
|
34
|
+
|
|
35
|
+
expect(commentButton.disabled).toBeFalsy();
|
|
36
|
+
|
|
37
|
+
await userEvent.clear(textInput);
|
|
38
|
+
|
|
39
|
+
expect(commentButton.disabled).toBeTruthy();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should clear text when cancel clicked', async () => {
|
|
43
|
+
const { getByPlaceholderText, getByRole } = renderCommentsAdd();
|
|
44
|
+
const textInput = getByPlaceholderText('Add a comment to this loan...');
|
|
45
|
+
const cancelButton = getByRole('button', { name: 'Cancel' });
|
|
46
|
+
|
|
47
|
+
await userEvent.type(textInput, 'test');
|
|
48
|
+
await userEvent.click(cancelButton);
|
|
49
|
+
|
|
50
|
+
expect(textInput.value).toBe('');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should emit value when comment clicked', async () => {
|
|
54
|
+
const { getByPlaceholderText, getByRole, emitted } = renderCommentsAdd();
|
|
55
|
+
const textInput = getByPlaceholderText('Add a comment to this loan...');
|
|
56
|
+
const commentButton = getByRole('button', { name: 'Comment' });
|
|
57
|
+
const TEST_INPUT = 'test test';
|
|
58
|
+
|
|
59
|
+
await userEvent.type(textInput, TEST_INPUT);
|
|
60
|
+
|
|
61
|
+
await userEvent.click(commentButton);
|
|
62
|
+
|
|
63
|
+
expect(emitted()[ADD_COMMENT_EVENT]).toEqual([[TEST_INPUT]]);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should not emit empty value when comment clicked', async () => {
|
|
67
|
+
const { getByRole, emitted } = renderCommentsAdd();
|
|
68
|
+
const commentButton = getByRole('button', { name: 'Comment' });
|
|
69
|
+
|
|
70
|
+
await userEvent.click(commentButton);
|
|
71
|
+
|
|
72
|
+
expect(emitted()[ADD_COMMENT_EVENT]).toBe(undefined);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { render } from '@testing-library/vue';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import Container from '../../../../vue/KvCommentsContainer.vue';
|
|
4
|
+
import { ADD_COMMENT_ID, ADD_COMMENT_EVENT } from '../../../../vue/KvCommentsAdd.vue';
|
|
5
|
+
|
|
6
|
+
const renderContainer = (props = {}) => {
|
|
7
|
+
return render(Container, { props });
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
describe('KvCommentsContainer', () => {
|
|
11
|
+
it('should render comments add component', async () => {
|
|
12
|
+
const { container } = renderContainer();
|
|
13
|
+
expect(container.querySelectorAll(`#${ADD_COMMENT_ID}`).length).toBe(1);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should emit comment', async () => {
|
|
17
|
+
const { getByPlaceholderText, getByRole, emitted } = renderContainer();
|
|
18
|
+
const textInput = getByPlaceholderText('Add a comment to this loan...');
|
|
19
|
+
const commentButton = getByRole('button', { name: 'Comment' });
|
|
20
|
+
const TEST_INPUT = 'test test';
|
|
21
|
+
|
|
22
|
+
await userEvent.type(textInput, TEST_INPUT);
|
|
23
|
+
|
|
24
|
+
await userEvent.click(commentButton);
|
|
25
|
+
|
|
26
|
+
expect(emitted()[ADD_COMMENT_EVENT]).toEqual([[TEST_INPUT]]);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="tw-flex tw-flex-col">
|
|
3
|
+
<div class="tw-flex tw-flex-col md:tw-flex-row md:tw-items-center tw-gap-0.5 md:tw-gap-1">
|
|
4
|
+
<div class="tw-flex tw-items-center tw-gap-1">
|
|
5
|
+
<img
|
|
6
|
+
v-if="userImageUrl"
|
|
7
|
+
:src="userImageUrl"
|
|
8
|
+
alt="Lender"
|
|
9
|
+
class="
|
|
10
|
+
data-hj-suppress
|
|
11
|
+
tw-inline-block
|
|
12
|
+
tw-w-3.5
|
|
13
|
+
tw-h-3.5
|
|
14
|
+
tw-rounded-full
|
|
15
|
+
tw-overflow-hidden
|
|
16
|
+
tw-object-fill
|
|
17
|
+
"
|
|
18
|
+
>
|
|
19
|
+
<div class="data-hj-suppress tw-font-medium">
|
|
20
|
+
{{ userDisplayName }}
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
<kv-text-input
|
|
24
|
+
:id="ADD_COMMENT_ID"
|
|
25
|
+
v-model="addCommentValue"
|
|
26
|
+
placeholder="Add a comment to this loan..."
|
|
27
|
+
class="data-hj-suppress tw-grow"
|
|
28
|
+
/>
|
|
29
|
+
</div>
|
|
30
|
+
<div class="tw-flex tw-py-0.5 tw-gap-0.5">
|
|
31
|
+
<kv-button
|
|
32
|
+
variant="ghost"
|
|
33
|
+
class="tw-ml-auto"
|
|
34
|
+
@click="cancel"
|
|
35
|
+
>
|
|
36
|
+
Cancel
|
|
37
|
+
</kv-button>
|
|
38
|
+
<kv-button
|
|
39
|
+
variant="ghost"
|
|
40
|
+
:state="commentButtonState"
|
|
41
|
+
@click="comment"
|
|
42
|
+
>
|
|
43
|
+
Comment
|
|
44
|
+
</kv-button>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
</template>
|
|
48
|
+
|
|
49
|
+
<script>
|
|
50
|
+
import { computed, ref } from 'vue-demi';
|
|
51
|
+
import KvButton from './KvButton.vue';
|
|
52
|
+
import KvTextInput from './KvTextInput.vue';
|
|
53
|
+
|
|
54
|
+
export const ADD_COMMENT_ID = 'add-comment-value';
|
|
55
|
+
export const ADD_COMMENT_EVENT = 'add-comment';
|
|
56
|
+
|
|
57
|
+
export default {
|
|
58
|
+
components: {
|
|
59
|
+
KvButton,
|
|
60
|
+
KvTextInput,
|
|
61
|
+
},
|
|
62
|
+
props: {
|
|
63
|
+
/**
|
|
64
|
+
* The full URL for the user image
|
|
65
|
+
*/
|
|
66
|
+
userImageUrl: {
|
|
67
|
+
type: String,
|
|
68
|
+
default: '',
|
|
69
|
+
},
|
|
70
|
+
/**
|
|
71
|
+
* The name to display for the user
|
|
72
|
+
*/
|
|
73
|
+
userDisplayName: {
|
|
74
|
+
type: String,
|
|
75
|
+
default: '',
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
emits: [ADD_COMMENT_EVENT],
|
|
79
|
+
setup(_props, { emit }) {
|
|
80
|
+
const addCommentValue = ref('');
|
|
81
|
+
|
|
82
|
+
const commentButtonState = computed(() => (addCommentValue.value ? '' : 'disabled'));
|
|
83
|
+
|
|
84
|
+
const cancel = () => {
|
|
85
|
+
addCommentValue.value = '';
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const comment = () => {
|
|
89
|
+
emit(ADD_COMMENT_EVENT, addCommentValue.value);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
ADD_COMMENT_ID,
|
|
94
|
+
addCommentValue,
|
|
95
|
+
commentButtonState,
|
|
96
|
+
cancel,
|
|
97
|
+
comment,
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
</script>
|
|
102
|
+
|
|
103
|
+
<style lang="postcss" scoped>
|
|
104
|
+
>>> input {
|
|
105
|
+
@apply tw-border-t-0 tw-border-r-0 tw-border-l-0 tw-rounded-none tw-p-0 tw-h-4;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
>>> input:focus {
|
|
109
|
+
@apply tw-border-tertiary;
|
|
110
|
+
box-shadow: none;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
>>> button > span {
|
|
114
|
+
@apply tw-min-h-0;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
>>> button > span > span {
|
|
118
|
+
@apply tw-py-0 tw-px-0.5;
|
|
119
|
+
}
|
|
120
|
+
</style>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<kv-comments-add
|
|
3
|
+
:user-image-url="userImageUrl"
|
|
4
|
+
:user-display-name="userDisplayName"
|
|
5
|
+
@add-comment="comment"
|
|
6
|
+
/>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script>
|
|
10
|
+
import KvCommentsAdd from './KvCommentsAdd.vue';
|
|
11
|
+
|
|
12
|
+
export const ADD_COMMENT_EVENT = 'add-comment';
|
|
13
|
+
|
|
14
|
+
export default {
|
|
15
|
+
components: {
|
|
16
|
+
KvCommentsAdd,
|
|
17
|
+
},
|
|
18
|
+
props: {
|
|
19
|
+
/**
|
|
20
|
+
* The full URL for the user image
|
|
21
|
+
*/
|
|
22
|
+
userImageUrl: {
|
|
23
|
+
type: String,
|
|
24
|
+
default: '',
|
|
25
|
+
},
|
|
26
|
+
/**
|
|
27
|
+
* The name to display for the user
|
|
28
|
+
*/
|
|
29
|
+
userDisplayName: {
|
|
30
|
+
type: String,
|
|
31
|
+
default: '',
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
setup(_props, { emit }) {
|
|
35
|
+
const comment = (commentValue) => {
|
|
36
|
+
emit(ADD_COMMENT_EVENT, commentValue);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
comment,
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
</script>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import KvCommentsAdd from '../KvCommentsAdd.vue';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
title: 'KvCommentsAdd',
|
|
5
|
+
component: KvCommentsAdd,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const story = (args) => {
|
|
9
|
+
const template = (templateArgs, { argTypes }) => ({
|
|
10
|
+
props: Object.keys(argTypes),
|
|
11
|
+
components: { KvCommentsAdd },
|
|
12
|
+
setup() { return { args: templateArgs }; },
|
|
13
|
+
template: `
|
|
14
|
+
<div style="max-width: 800px;">
|
|
15
|
+
<KvCommentsAdd v-bind="args" />
|
|
16
|
+
</div>
|
|
17
|
+
`,
|
|
18
|
+
});
|
|
19
|
+
template.args = args;
|
|
20
|
+
return template;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const Default = story({});
|
|
24
|
+
|
|
25
|
+
export const UserName = story({
|
|
26
|
+
userDisplayName: 'User',
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const UserInfo = story({
|
|
30
|
+
userImageUrl: 'https://via.placeholder.com/50x50',
|
|
31
|
+
userDisplayName: 'User',
|
|
32
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import KvCommentsContainer from '../KvCommentsContainer.vue';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
title: 'KvCommentsContainer',
|
|
5
|
+
component: KvCommentsContainer,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const story = (args) => {
|
|
9
|
+
const template = (templateArgs, { argTypes }) => ({
|
|
10
|
+
props: Object.keys(argTypes),
|
|
11
|
+
components: { KvCommentsContainer },
|
|
12
|
+
setup() { return { args: templateArgs }; },
|
|
13
|
+
template: `
|
|
14
|
+
<div style="max-width: 800px;">
|
|
15
|
+
<KvCommentsContainer v-bind="args" />
|
|
16
|
+
</div>
|
|
17
|
+
`,
|
|
18
|
+
});
|
|
19
|
+
template.args = args;
|
|
20
|
+
return template;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const Default = story({});
|