@kiva/kv-components 3.16.0 → 3.17.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 +11 -0
- package/package.json +8 -3
- package/postcss.config.cjs +2 -2
- package/tests/unit/specs/components/KvLendAmountButton.spec.js +36 -0
- package/tests/unit/specs/components/KvLoanProgressGroup.spec.js +50 -0
- package/vue/.storybook/main.js +45 -0
- package/vue/.storybook/package.json +3 -0
- package/vue/.storybook/preview.js +5 -1
- package/vue/KvBorrowerImage.vue +170 -0
- package/vue/KvClassicLoanCard.vue +462 -0
- package/vue/KvLendAmountButton.vue +76 -0
- package/vue/KvLendCta.vue +382 -0
- package/vue/KvLoanBookmark.vue +39 -0
- package/vue/KvLoanCallouts.vue +53 -0
- package/vue/KvLoanProgressGroup.vue +56 -0
- package/vue/KvLoanTag.vue +108 -0
- package/vue/KvLoanUse.vue +70 -0
- package/vue/stories/KvBorrowerImage.stories.js +62 -0
- package/vue/stories/KvClassicLoanCard.stories.js +206 -0
- package/vue/stories/KvLendAmountButton.stories.js +34 -0
- package/vue/stories/KvLendCta.stories.js +104 -0
- package/vue/stories/KvLoanBookmark.stories.js +22 -0
- package/vue/stories/KvLoanCallouts.stories.js +22 -0
- package/vue/stories/KvLoanProgressGroup.stories.js +29 -0
- package/vue/stories/KvLoanTag.stories.js +51 -0
- package/vue/stories/KvLoanUse.stories.js +51 -0
- package/vue/.storybook/main.cjs +0 -33
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
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.17.0](https://github.com/kiva/kv-ui-elements/compare/@kiva/kv-components@3.16.0...@kiva/kv-components@3.17.0) (2023-05-05)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* new loan card ported from recent successful Denali experiment ([0b0e5a0](https://github.com/kiva/kv-ui-elements/commit/0b0e5a0a575bc1c65ab71579f151a6d3cee5d8d7))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
6
17
|
# [3.16.0](https://github.com/kiva/kv-ui-elements/compare/@kiva/kv-components@3.15.0...@kiva/kv-components@3.16.0) (2023-05-03)
|
|
7
18
|
|
|
8
19
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kiva/kv-components",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.17.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"babel-jest": "^27.0.6",
|
|
29
29
|
"babel-loader": "^8.2.2",
|
|
30
30
|
"chromatic": "^5.9.2",
|
|
31
|
+
"css-loader": "^5.2.7",
|
|
31
32
|
"eslint": "^7.26.0",
|
|
32
33
|
"eslint-config-airbnb-base": "^14.1.0",
|
|
33
34
|
"eslint-plugin-import": "^2.20.2",
|
|
@@ -36,7 +37,8 @@
|
|
|
36
37
|
"jest": "^27.4.3",
|
|
37
38
|
"jest-axe": "^5.0.1",
|
|
38
39
|
"postcss": "^8.4.5",
|
|
39
|
-
"
|
|
40
|
+
"postcss-loader": "^4.3.0",
|
|
41
|
+
"style-loader": "^2.0.0",
|
|
40
42
|
"tailwindcss": "^3.0.18",
|
|
41
43
|
"vue": "^2.6.14",
|
|
42
44
|
"vue-loader": "^15.9.6",
|
|
@@ -51,14 +53,17 @@
|
|
|
51
53
|
"build": "echo No build needed for @kiva/kv-components."
|
|
52
54
|
},
|
|
53
55
|
"dependencies": {
|
|
56
|
+
"@chenfengyuan/vue-countdown": "^1.1.5",
|
|
54
57
|
"@kiva/kv-tokens": "^2.7.0",
|
|
55
58
|
"@mdi/js": "^5.9.55",
|
|
56
59
|
"@vueuse/integrations": "^7.6.0",
|
|
57
60
|
"aria-hidden": "^1.1.3",
|
|
58
61
|
"change-case": "^4.1.2",
|
|
62
|
+
"date-fns": "^2.30.0",
|
|
59
63
|
"embla-carousel": "^4.5.3",
|
|
60
64
|
"focus-trap": "^6.7.2",
|
|
61
65
|
"nanoid": "^3.1.23",
|
|
66
|
+
"numeral": "^2.0.6",
|
|
62
67
|
"vue-demi": "^0.12.1"
|
|
63
68
|
},
|
|
64
69
|
"peerDependencies": {
|
|
@@ -70,5 +75,5 @@
|
|
|
70
75
|
"optional": true
|
|
71
76
|
}
|
|
72
77
|
},
|
|
73
|
-
"gitHead": "
|
|
78
|
+
"gitHead": "9d1eae452258b88c989f42b816aa99f87144041a"
|
|
74
79
|
}
|
package/postcss.config.cjs
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { render } from '@testing-library/vue';
|
|
2
|
+
import KvLendAmountButton from '../../../../vue/KvLendAmountButton.vue';
|
|
3
|
+
|
|
4
|
+
describe('KvLendAmountButton', () => {
|
|
5
|
+
it('should display amount with a number input', async () => {
|
|
6
|
+
const { getByText } = render(KvLendAmountButton,
|
|
7
|
+
{
|
|
8
|
+
provide: {
|
|
9
|
+
apollo: {
|
|
10
|
+
query: () => Promise.resolve({}),
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
props: { amountLeft: 20.00 },
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
// Expect the button to exist and have correct amount
|
|
17
|
+
const buttonElement = getByText('Lend $20');
|
|
18
|
+
expect(buttonElement).toBeDefined();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should display amount with a string input', async () => {
|
|
22
|
+
const { getByText } = render(KvLendAmountButton,
|
|
23
|
+
{
|
|
24
|
+
provide: {
|
|
25
|
+
apollo: {
|
|
26
|
+
query: () => Promise.resolve({}),
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
props: { amountLeft: '20.00' },
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Expect the button to exist and have correct amount
|
|
33
|
+
const buttonElement = getByText('Lend $20');
|
|
34
|
+
expect(buttonElement).toBeDefined();
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { render } from '@testing-library/vue';
|
|
2
|
+
import KvLoanProgressGroup from '../../../../vue/KvLoanProgressGroup.vue';
|
|
3
|
+
|
|
4
|
+
describe('KvLoanProgressGroup', () => {
|
|
5
|
+
it('should display default message', () => {
|
|
6
|
+
const { getByText } = render(KvLoanProgressGroup);
|
|
7
|
+
|
|
8
|
+
getByText('funded!');
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('should display amount left', () => {
|
|
12
|
+
const { getByText } = render(KvLoanProgressGroup, {
|
|
13
|
+
props: {
|
|
14
|
+
moneyLeft: '12.34',
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
getByText('$12.34 to go!');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should use orange color with low amount', () => {
|
|
22
|
+
const { container } = render(KvLoanProgressGroup, {
|
|
23
|
+
props: {
|
|
24
|
+
moneyLeft: '12.34',
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
expect(container.getElementsByClassName('progress-group-amount-low').length).toBe(1);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should not use orange color with not low amount', () => {
|
|
32
|
+
const { container } = render(KvLoanProgressGroup, {
|
|
33
|
+
props: {
|
|
34
|
+
moneyLeft: '500',
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
expect(container.getElementsByClassName('progress-group-amount-low').length).toBe(0);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should pass percentage to progress bar', () => {
|
|
42
|
+
const { container } = render(KvLoanProgressGroup, {
|
|
43
|
+
props: {
|
|
44
|
+
progressPercent: 0.5,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
expect(container.querySelector('[role="progressbar"]').getAttribute('aria-valuenow')).toBe('50');
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
stories: [
|
|
5
|
+
'../stories/Styleguide.stories.js', // show the base styleguide first
|
|
6
|
+
'../stories/**/*.stories.mdx',
|
|
7
|
+
'../stories/**/*.stories.@(js|jsx|ts|tsx)'
|
|
8
|
+
],
|
|
9
|
+
addons: [
|
|
10
|
+
'@storybook/addon-links',
|
|
11
|
+
'@storybook/addon-essentials',
|
|
12
|
+
'@storybook/addon-a11y',
|
|
13
|
+
'@storybook/addon-storysource',
|
|
14
|
+
'@storybook/addon-postcss',
|
|
15
|
+
],
|
|
16
|
+
webpackFinal: async (config) => {
|
|
17
|
+
config.module.rules.push({
|
|
18
|
+
test: /\.mjs$/,
|
|
19
|
+
include: /node_modules/,
|
|
20
|
+
type: 'javascript/auto'
|
|
21
|
+
});
|
|
22
|
+
config.module.rules.push({
|
|
23
|
+
test: /\.postcss$/,
|
|
24
|
+
use: [
|
|
25
|
+
'style-loader',
|
|
26
|
+
{
|
|
27
|
+
loader: 'css-loader',
|
|
28
|
+
options: { importLoaders: 1, sourceMap: false },
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
loader: 'postcss-loader',
|
|
32
|
+
options: {
|
|
33
|
+
postcssOptions: {
|
|
34
|
+
plugins: [
|
|
35
|
+
require('tailwindcss'),
|
|
36
|
+
require('autoprefixer'),
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
});
|
|
43
|
+
return config;
|
|
44
|
+
},
|
|
45
|
+
}
|
|
@@ -4,10 +4,13 @@ import KvThemeProvider from '../KvThemeProvider.vue';
|
|
|
4
4
|
import { defaultTheme, darkTheme } from '@kiva/kv-tokens/configs/kivaColors.cjs';
|
|
5
5
|
import Vue from 'vue';
|
|
6
6
|
import VueCompositionApi from '@vue/composition-api';
|
|
7
|
+
import VueRouter from 'vue-router';
|
|
7
8
|
|
|
8
9
|
// Add vue composition api
|
|
9
10
|
Vue.use(VueCompositionApi);
|
|
10
11
|
|
|
12
|
+
Vue.use(VueRouter);
|
|
13
|
+
|
|
11
14
|
export const parameters = {
|
|
12
15
|
actions: { argTypesRegex: "^on[A-Z].*" },
|
|
13
16
|
controls: {
|
|
@@ -46,5 +49,6 @@ export const decorators = [(story) => ({
|
|
|
46
49
|
},
|
|
47
50
|
destroyed() {
|
|
48
51
|
channel.off('DARK_MODE', this.setTheme);
|
|
49
|
-
}
|
|
52
|
+
},
|
|
53
|
+
router: new VueRouter(),
|
|
50
54
|
})];
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<picture
|
|
3
|
+
class="
|
|
4
|
+
tw-inline-block
|
|
5
|
+
tw-relative
|
|
6
|
+
tw-overflow-hidden
|
|
7
|
+
tw-w-full
|
|
8
|
+
tw-bg-black
|
|
9
|
+
"
|
|
10
|
+
:style="`padding-bottom: ${aspectRatio * 100}%;`"
|
|
11
|
+
>
|
|
12
|
+
<source
|
|
13
|
+
:srcset="srcset"
|
|
14
|
+
:sizes="sizes"
|
|
15
|
+
>
|
|
16
|
+
<img
|
|
17
|
+
v-if="hash"
|
|
18
|
+
class="
|
|
19
|
+
tw-absolute
|
|
20
|
+
tw-w-full
|
|
21
|
+
tw-h-full
|
|
22
|
+
tw-object-contain
|
|
23
|
+
"
|
|
24
|
+
:src="defaultUrl"
|
|
25
|
+
:alt="alt"
|
|
26
|
+
loading="lazy"
|
|
27
|
+
>
|
|
28
|
+
</picture>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<script>
|
|
32
|
+
export default {
|
|
33
|
+
name: 'KvBorrowerImage',
|
|
34
|
+
props: {
|
|
35
|
+
/**
|
|
36
|
+
* String to use as the alternative of this image for screen readers.
|
|
37
|
+
*/
|
|
38
|
+
alt: {
|
|
39
|
+
type: String,
|
|
40
|
+
required: true,
|
|
41
|
+
},
|
|
42
|
+
/**
|
|
43
|
+
* Number to use as the aspect ratio for this image. Defined as height / width.
|
|
44
|
+
*/
|
|
45
|
+
aspectRatio: {
|
|
46
|
+
type: Number,
|
|
47
|
+
default: 1,
|
|
48
|
+
},
|
|
49
|
+
/**
|
|
50
|
+
* Size properties of the default image to use as a fallback.
|
|
51
|
+
* Sample object:
|
|
52
|
+
* {
|
|
53
|
+
width: 320,
|
|
54
|
+
faceZoom: 50, // optional
|
|
55
|
+
}
|
|
56
|
+
*/
|
|
57
|
+
defaultImage: {
|
|
58
|
+
type: Object,
|
|
59
|
+
required: true,
|
|
60
|
+
},
|
|
61
|
+
/**
|
|
62
|
+
* String of the hash of the image, used to build the image urls.
|
|
63
|
+
*/
|
|
64
|
+
hash: {
|
|
65
|
+
type: String,
|
|
66
|
+
required: true,
|
|
67
|
+
},
|
|
68
|
+
/**
|
|
69
|
+
* Array of image objects containing their size and their intended viewport display width.
|
|
70
|
+
* Sample image object:
|
|
71
|
+
* {
|
|
72
|
+
width: 280, // width of the image at 1x
|
|
73
|
+
viewSize: 320, // viewport width at which this size should be displayed
|
|
74
|
+
faceZoom: 50, // optional
|
|
75
|
+
}
|
|
76
|
+
*/
|
|
77
|
+
images: {
|
|
78
|
+
type: Array,
|
|
79
|
+
default: () => [],
|
|
80
|
+
},
|
|
81
|
+
/**
|
|
82
|
+
* The base path to source the image
|
|
83
|
+
*/
|
|
84
|
+
photoPath: {
|
|
85
|
+
type: String,
|
|
86
|
+
required: true,
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
computed: {
|
|
90
|
+
// Get the full url for the fallback image
|
|
91
|
+
defaultUrl() {
|
|
92
|
+
if (!this.hash) {
|
|
93
|
+
return '';
|
|
94
|
+
}
|
|
95
|
+
return this.getImgUrl({
|
|
96
|
+
...this.defaultImage,
|
|
97
|
+
height: this.defaultImage.width * this.aspectRatio,
|
|
98
|
+
});
|
|
99
|
+
},
|
|
100
|
+
// Get the 'sizes' string for the source element
|
|
101
|
+
sizes() {
|
|
102
|
+
if (!this.hash) {
|
|
103
|
+
return '';
|
|
104
|
+
}
|
|
105
|
+
return this.images.map(({ width, viewSize }) => {
|
|
106
|
+
if (viewSize) {
|
|
107
|
+
return `(min-width: ${viewSize}px) ${width}px`;
|
|
108
|
+
}
|
|
109
|
+
return `${width}px`;
|
|
110
|
+
}).join(', ');
|
|
111
|
+
},
|
|
112
|
+
// Get the 'srcset' string for the source element
|
|
113
|
+
srcset() {
|
|
114
|
+
if (!this.hash) {
|
|
115
|
+
return '';
|
|
116
|
+
}
|
|
117
|
+
return this.images.map(({ width, faceZoom }) => {
|
|
118
|
+
const height = width * this.aspectRatio;
|
|
119
|
+
const size = {
|
|
120
|
+
width,
|
|
121
|
+
height,
|
|
122
|
+
faceZoom,
|
|
123
|
+
};
|
|
124
|
+
const retinaSize = {
|
|
125
|
+
width: width * 2,
|
|
126
|
+
height: height * 2,
|
|
127
|
+
faceZoom,
|
|
128
|
+
};
|
|
129
|
+
return `${this.getSrcsetDef(size)}, ${this.getSrcsetDef(retinaSize)}`;
|
|
130
|
+
}).join(', ');
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
methods: {
|
|
134
|
+
getKivaImageUrl({
|
|
135
|
+
base = '/',
|
|
136
|
+
width,
|
|
137
|
+
height,
|
|
138
|
+
square,
|
|
139
|
+
faceZoom,
|
|
140
|
+
hash,
|
|
141
|
+
format = 'jpg',
|
|
142
|
+
} = {}) {
|
|
143
|
+
if (!hash) {
|
|
144
|
+
return '';
|
|
145
|
+
}
|
|
146
|
+
if (!width && !height && !square && !faceZoom) {
|
|
147
|
+
return '';
|
|
148
|
+
}
|
|
149
|
+
const w = width ? `w${Math.ceil(width)}` : '';
|
|
150
|
+
const h = height ? `h${Math.ceil(height)}` : '';
|
|
151
|
+
const s = square ? `s${Math.ceil(square)}` : '';
|
|
152
|
+
const fz = faceZoom ? `fz${Math.ceil(faceZoom)}` : '';
|
|
153
|
+
|
|
154
|
+
return `${base}${w}${h}${s}${fz}/${hash}.${format}`;
|
|
155
|
+
},
|
|
156
|
+
// Get the url for the loan image sized width by height
|
|
157
|
+
getImgUrl(size) {
|
|
158
|
+
return this.getKivaImageUrl({
|
|
159
|
+
...size,
|
|
160
|
+
base: this.photoPath,
|
|
161
|
+
hash: this.hash,
|
|
162
|
+
});
|
|
163
|
+
},
|
|
164
|
+
// Get a string to use in the srcset attribute as the definition for a single image size
|
|
165
|
+
getSrcsetDef(size) {
|
|
166
|
+
return `${this.getImgUrl(size)} ${size.width}w`;
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
</script>
|