@kiva/kv-components 3.16.0 → 3.18.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 CHANGED
@@ -3,6 +3,28 @@
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.18.0](https://github.com/kiva/kv-ui-elements/compare/@kiva/kv-components@3.17.0...@kiva/kv-components@3.18.0) (2023-05-05)
7
+
8
+
9
+ ### Features
10
+
11
+ * "view loan" CTA ([aa10cf1](https://github.com/kiva/kv-ui-elements/commit/aa10cf17a1ca4978b97fc9541522f31d95cc886f))
12
+
13
+
14
+
15
+
16
+
17
+ # [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)
18
+
19
+
20
+ ### Features
21
+
22
+ * new loan card ported from recent successful Denali experiment ([0b0e5a0](https://github.com/kiva/kv-ui-elements/commit/0b0e5a0a575bc1c65ab71579f151a6d3cee5d8d7))
23
+
24
+
25
+
26
+
27
+
6
28
  # [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
29
 
8
30
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kiva/kv-components",
3
- "version": "3.16.0",
3
+ "version": "3.18.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
- "storybook-dark-mode": "^1.0.8",
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": "c442c7a5a998b36082f5ed00addaa6c43c857d74"
78
+ "gitHead": "68db3fee0d19c1708e9ac97cf69e04ca97fadd62"
74
79
  }
@@ -1,6 +1,6 @@
1
1
  module.exports = {
2
2
  plugins: {
3
3
  tailwindcss: {},
4
- autoprefixer: {}
5
- }
4
+ autoprefixer: {},
5
+ },
6
6
  };
@@ -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
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
@@ -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>