@vcita/design-system 1.1.6 → 1.1.7-beta.1

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.
@@ -43,7 +43,10 @@ import {
43
43
  VBadge,
44
44
  VRadioGroup,
45
45
  VRadio,
46
-
46
+ VTabs,
47
+ VTab,
48
+ VTabsItems,
49
+ VTabItem,
47
50
  } from "vuetify/lib/components";
48
51
  // import initI18n from "./initI18n";
49
52
  import svgImages from "./svgImages";
@@ -91,7 +94,10 @@ Vue.use(Vuetify, {
91
94
  VBadge,
92
95
  VRadioGroup,
93
96
  VRadio,
94
-
97
+ VTabs,
98
+ VTab,
99
+ VTabsItems,
100
+ VTabItem,
95
101
  }
96
102
  });
97
103
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vcita/design-system",
3
- "version": "1.1.6",
3
+ "version": "1.1.7-beta.1",
4
4
  "description": "vcita design system",
5
5
  "author": "vcita",
6
6
  "scripts": {
@@ -47,7 +47,7 @@ import VcIcon from "@/components/VcIcon/VcIcon.vue";
47
47
  import VcButton from "../VcButton/VcButton.vue";
48
48
  const FORMATS = {
49
49
  DOCUMENT: ['doc', 'docx'],
50
- IMAGE: ['jpg', 'jpeg', 'png', 'gif', 'bmp'],
50
+ IMAGE: ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg'],
51
51
  PDF: ['pdf'],
52
52
  SPREADSHEET: ['csv', 'xls', 'xlsx'],
53
53
  SLIDESHOW: ['ppt', 'pptx'],
@@ -166,4 +166,19 @@ describe("VcSearchPicker.vue", () => {
166
166
  await updateProps({ loading: false });
167
167
  expect(spinner).not.toBeInTheDocument();
168
168
  });
169
+
170
+ it("Renders custom menu class name", async () => {
171
+ const CLASSNAME_A = 'abc'
172
+ const CLASSNAME_B = 'def'
173
+ const { container, updateProps } = renderWithVuetify(VcSearchPicker, {
174
+ props: { menuContentClass: CLASSNAME_A }
175
+ });
176
+ const autoComplete = await container.getElementsByClassName('autocomplete')[0];
177
+ await fireEvent.click(autoComplete);
178
+ const menuContent = await container.querySelector('.v-menu__content');
179
+ expect(menuContent).toHaveClass(CLASSNAME_A);
180
+ await updateProps({ menuContentClass: CLASSNAME_B });
181
+ expect(menuContent).not.toHaveClass(CLASSNAME_A);
182
+ expect(menuContent).toHaveClass(CLASSNAME_B);
183
+ });
169
184
  });
@@ -20,6 +20,7 @@ const Template = (args, {argTypes}) => ({
20
20
  :showInnerIcon="showInnerIcon"
21
21
  :items="this.items"
22
22
  :selectedItems="exampleSelected"
23
+ :menuContentClass="menuContentClass"
23
24
  @onSelectionChange="newVal => this.exampleSelected = newVal"
24
25
  @onSearchInput="searchInput"
25
26
  @onScrollEnd="scrollEnd"
@@ -54,6 +55,7 @@ const Template2 = (args, {argTypes}) => ({
54
55
  :showInnerIcon="showInnerIcon"
55
56
  :items="this.items"
56
57
  :selectedItems="exampleSelected"
58
+ :menuContentClass="menuContentClass"
57
59
  @onSelectionChange="newVal => this.exampleSelected = newVal"
58
60
  @onSearchInput="searchInput"
59
61
  @onScrollEnd="scrollEnd"
@@ -74,6 +76,7 @@ DocumentItem.args = {
74
76
  errorLabelText: 'Please select A document',
75
77
  noDataText: 'No documents found',
76
78
  iconType: 'folder',
79
+ menuContentClass: 'custom- content- class',
77
80
  }
78
81
 
79
82
 
@@ -94,6 +97,7 @@ const Template3 = (args, {argTypes}) => ({
94
97
  :showInnerIcon="showInnerIcon"
95
98
  :items="this.items"
96
99
  :selectedItems="exampleSelected"
100
+ :menuContentClass="menuContentClass"
97
101
  @onSelectionChange="newVal => this.exampleSelected = newVal"
98
102
  @onSearchInput="searchInput"
99
103
  @onScrollEnd="scrollEnd"
@@ -138,6 +142,7 @@ const Template4 = (args, {argTypes}) => ({
138
142
  :showInnerIcon="showInnerIcon"
139
143
  :items="this.items"
140
144
  v-model="exampleSelected"
145
+ :menuContentClass="menuContentClass"
141
146
  @onSelectionChange="selectionChange"
142
147
  @onSearchInput="searchInput"
143
148
  @onScrollEnd="scrollEnd"
@@ -10,6 +10,7 @@
10
10
  'autocomplete--has-items': selectedItems.length,
11
11
  'autocomplete--search-content': searchInput && searchInput.length,
12
12
  }"
13
+ :menu-content-class="menuContentClass"
13
14
  :menu-props="menuProps"
14
15
  :search-input.sync="searchInput"
15
16
  @update:search-input="(text) => text && text.length && $emit('onSearchInput', text)"
@@ -105,6 +106,10 @@ export default {
105
106
  error: {
106
107
  type: Boolean,
107
108
  default: false,
109
+ },
110
+ menuContentClass: {
111
+ type: String,
112
+ default: ''
108
113
  }
109
114
  },
110
115
  data() {
@@ -120,7 +125,7 @@ export default {
120
125
  menuProps() {
121
126
  return {
122
127
  value: this.focused,
123
- contentClass: 'vc-select-field-menu'
128
+ contentClass: `vc-select-field-menu ${this.menuContentClass}`
124
129
  }
125
130
  },
126
131
  noData() {
@@ -173,14 +178,15 @@ export default {
173
178
  }
174
179
  .v-input__icon--append .v-icon {
175
180
  transform: rotate(0deg);
181
+ color: var(--gray-darken-3)!important;
176
182
  }
177
183
  .autocomplete--open {
178
184
  .v-input__icon--append .v-icon {
179
185
  transform: rotate(180deg)
180
186
  }
181
187
  .v-input__slot {
182
- border-color: var(--search-picker-border-color);
183
- outline: 3px solid var(--search-picker-second-border-color);
188
+ border-color: var(--v-primary-base);
189
+ outline: 2px solid var(--v-primary-lighten5);
184
190
  }
185
191
  }
186
192
  .autocomplete {
@@ -241,6 +247,7 @@ export default {
241
247
  display: inline-block;
242
248
  font-size: var(--font-size-small2);
243
249
  color: var(--gray-darken-5);
250
+ caret-color: currentColor;
244
251
  }
245
252
  }
246
253
 
@@ -12,7 +12,7 @@ const characters = [
12
12
  const documents = [
13
13
  { text: 'Client Contract.pdf', value: 'f22fbda2-637e-4df5-9106-435dc8ade089'},
14
14
  { text: 'Brochure.jpeg', value: '03f95bb8-8435-499f-9ca7-efb23a3692f2'},
15
- { text: 'Application and Offer Logic And a very long text', value: '13a34bf5-230e-481b-9808-d2d6ac4364dd' },
15
+ { text: 'Application and Offer Logic And a very long text'.repeat(10), value: '13a34bf5-230e-481b-9808-d2d6ac4364dd' },
16
16
  { text: 'Great music.mp3', value: '758f20ee-499e-4892-abb5-65626bd31b35' },
17
17
  { text: 'Client Contract1.xls', value: '84e3c394-d92a-4a21-8b05-5680c3f10eb0' },
18
18
  { text: 'Brochure.pptx', value: '17efc973-8a0f-4b6e-a3a1-db65d663032f' },
@@ -0,0 +1,57 @@
1
+ import '@testing-library/jest-dom'
2
+ import VcTabs from "./VcTabs.vue";
3
+ import Vue from 'vue'
4
+ import Vuetify from 'vuetify'
5
+ import {render} from "@testing-library/vue";
6
+ import userEvent from '@testing-library/user-event';
7
+
8
+ Vue.use(Vuetify)
9
+
10
+ describe("VcTabs.vue", () => {
11
+
12
+ const renderWithVuetify = (component, options, callback, isMobile = false) => {
13
+ const root = document.createElement('div')
14
+ root.setAttribute('data-app', 'true')
15
+
16
+ const vuetify = new Vuetify()
17
+ if (isMobile) {
18
+ const breakpoint = {
19
+ init: jest.fn(),
20
+ framework: {},
21
+ smAndDown: true,
22
+ }
23
+
24
+ vuetify.framework.breakpoint = breakpoint;
25
+ }
26
+
27
+ return render(
28
+ component,
29
+ {
30
+ container: document.body.appendChild(root),
31
+ // for Vuetify components that use the vuetify instance property
32
+ vuetify,
33
+ ...options,
34
+ mocks: {
35
+ $t: value => value,
36
+ $dst: value => value, // <- for the design system
37
+ },
38
+ },
39
+ callback,
40
+ )
41
+ }
42
+
43
+ it("mounts", () => {
44
+ // Queries: https://testing-library.com/docs/queries/about#types-of-queries
45
+ const {container, getByTestId} = renderWithVuetify(VcTabs, {
46
+ props: {}
47
+ })
48
+
49
+ // Expect options: https://github.com/testing-library/jest-dom
50
+ expect(container).toHaveAttribute('data-app', 'true');
51
+
52
+ // Make sure the component has the data-qa attribute
53
+ const component = getByTestId('VcTabs');
54
+ expect(component).toBeInTheDocument();
55
+ });
56
+
57
+ });
@@ -0,0 +1,182 @@
1
+ import VcTabsCmp from './VcTabs';
2
+ import VcForm from "@/components/VcForm/VcForm";
3
+ import VcTextField from "@/components/VcTextField/VcTextField";
4
+ import VcImage from "@/components/VcImage/VcImage";
5
+ import VcLoader from "@/components/VcLoader/VcLoader";
6
+ import VcIcon from "@/components/VcIcon/VcIcon";
7
+
8
+ const basicProps = {
9
+ tabs: [{label: 'Tab 1'}, {label: 'Tab 2'}, {label: 'Tab 3'}, {label: 'Tab 4'},],
10
+ distribution: 'start',
11
+ tabSize: 'M',
12
+ separatorStyle: 'default',
13
+ }
14
+
15
+ const Template = (args, {argTypes}) => ({
16
+ components: {VcTabs: VcTabsCmp},
17
+ props: Object.keys(argTypes),
18
+ data: () => ({
19
+ currentIndex: null,
20
+ }),
21
+ template: `
22
+ <div>
23
+ Known issue: When using the tab key in the keyboard to navigate between tabs, you can reach disabled tabs.
24
+ This issue is fixed in Vuetify 2.6.8, and so will be fixed once we upgrade the Vuetify version.
25
+ <VcTabs :tabs="tabs"
26
+ :distribution="distribution"
27
+ :tab-size="tabSize"
28
+ :separator-style="separatorStyle"
29
+ v-model="currentIndex"
30
+ @change="onChange">
31
+ </VcTabs>
32
+ </div>`,
33
+ });
34
+
35
+ export const Playground = Template.bind({});
36
+
37
+ // Set default values
38
+ Playground.args = {
39
+ ...basicProps,
40
+ tabs: [{label: 'Tab 1'}, {label: 'Tab 2'}, {label: 'Tab 3', disabled: true}, {label: 'Tab 4'},],
41
+ };
42
+
43
+ const UsingTabSlotTemplate = (args, {argTypes}) => ({
44
+ components: {VcTabs: VcTabsCmp, VcIcon},
45
+ props: Object.keys(argTypes),
46
+ data: () => ({
47
+ currentIndex: null,
48
+ }),
49
+ template: `
50
+ <div>
51
+ <VcTabs :tabs="tabs"
52
+ v-model="currentIndex"
53
+ :distribution="distribution"
54
+ :tab-size="tabSize"
55
+ :separator-style="separatorStyle"
56
+ @change="onChange">
57
+ <template #tab-0>
58
+ <VcIcon color="black">$success</VcIcon>
59
+ </template>
60
+ <template #tab-1>
61
+ <VcIcon color="black">$plus</VcIcon>
62
+ </template>
63
+ <template #tab-2>
64
+ <VcIcon color="black">$magnify_glass</VcIcon>
65
+ </template>
66
+ <template #tab-3>
67
+ <VcIcon color="black">$corrupted_flag</VcIcon>
68
+ </template>
69
+ </VcTabs>
70
+ </div>`,
71
+ });
72
+
73
+ export const UsingTabSlot = UsingTabSlotTemplate.bind({});
74
+
75
+ // Set default values
76
+ UsingTabSlot.args = {
77
+ ...basicProps,
78
+ };
79
+
80
+ const Initialized = (args, {argTypes}) => ({
81
+ components: {VcTabs: VcTabsCmp, VcForm, VcTextField, VcImage, VcLoader},
82
+ props: Object.keys(argTypes),
83
+ data: () => ({
84
+ currentIndex: 1,
85
+ }),
86
+ template: `
87
+ <div>
88
+ <VcTabs :tabs="tabs" v-model="currentIndex"
89
+ :distribution="distribution"
90
+ :tab-size="tabSize"
91
+ :separator-style="separatorStyle"
92
+ @change="onChange">
93
+ <template #tab-content-0="data">
94
+ <div>{{ data.item.label }}</div>
95
+ <div>This tab is an example of how you can access the title of the tab, if you need to</div>
96
+ </template>
97
+ <template #tab-content-1>
98
+ <div>You can put anything you want in your tab</div>
99
+ <VcLoader/>
100
+ </template>
101
+ <template #tab-content-2>
102
+ <div>Really, even an image</div>
103
+ <VcImage image="static/media/src/stories/assets/upgrade.svg"/>
104
+ </template>
105
+ <template #tab-content-3>
106
+ <div>Or a form, it's totally flexible</div>
107
+ <template>
108
+ <div>
109
+ <VcForm>
110
+ <VcTextField
111
+ label="Name"
112
+ :counter="10"/>
113
+ </VcForm>
114
+ </div>
115
+ </template>
116
+ </template>
117
+ </VcTabs>
118
+ </div>`,
119
+ });
120
+
121
+ export const SelectInitialTab = Initialized.bind({});
122
+
123
+ SelectInitialTab.args = {
124
+ ...basicProps,
125
+ };
126
+
127
+ const BGColorTemplate = (args, {argTypes}) => ({
128
+ components: {VcTabs: VcTabsCmp},
129
+ props: Object.keys(argTypes),
130
+ data: () => ({
131
+ currentIndex: null,
132
+ }),
133
+ template: `
134
+ <div style="background-color: #aaee93">
135
+ The elements of the component are all background-color: transparent
136
+ That means that they will receive the color of the elements behind it.
137
+ <VcTabs :tabs="tabs"
138
+ :distribution="distribution"
139
+ :tab-size="tabSize"
140
+ :separator-style="separatorStyle"
141
+ v-model="currentIndex"
142
+ @change="onChange">
143
+ </VcTabs>
144
+ </div>`,
145
+ });
146
+
147
+ export const BgColor = BGColorTemplate.bind({});
148
+
149
+ BgColor.args = {
150
+ ...basicProps,
151
+ };
152
+
153
+ export default {
154
+ title: 'containers / VcTabs', // This will control the story sidebar position
155
+ id: 'VcTabs', // This will be the permanent link to this component
156
+ component: VcTabsCmp,
157
+ argTypes: {
158
+ distribution: {
159
+ options: ['start', 'equal', 'grow'],
160
+ control: {type: 'radio'}
161
+ },
162
+ tabSize: {
163
+ options: ['M', 'L'],
164
+ control: {type: 'radio'}
165
+ },
166
+ separatorStyle: {
167
+ options: ['default','shadow', 'divider'],
168
+ control: {type: 'radio'}
169
+ },
170
+ onChange: {action: 'change', table: {disable: true}},
171
+ },
172
+ parameters: {
173
+ design: {
174
+ type: 'figma',
175
+ url: 'https://www.figma.com/file/xIOY6fBoA1wpy1tHv3i5js/vcita---ui-library?node-id=251%3A912',
176
+ },
177
+ status: {
178
+ type: 'beta', // 'beta' | 'stable' | 'deprecated' | 'releaseCandidate'
179
+ url: 'https://vuetifyjs.com/en/components/tabs',
180
+ },
181
+ },
182
+ };
@@ -0,0 +1,125 @@
1
+ <template>
2
+ <div :data-qa="dataQa" class="VcTabs--wrapper">
3
+ <v-tabs :value="safeCurrentIndex"
4
+ background-color="transparent"
5
+ slider-color="secondary"
6
+ slider-size="4"
7
+ :height="tabSize === 'M' ? 48 : 60"
8
+ :grow="distribution === 'grow'"
9
+ :fixed-tabs="distribution==='equal'"
10
+ class="VcTabs--content"
11
+ :class="{'separatorShadow': separatorStyle === 'shadow', 'separatorDivider' : separatorStyle === 'divider'}"
12
+ @change="(newIndex) => $emit('change', newIndex)">
13
+ <v-tab v-for="(item, index) in tabs" :key="`tab-${index}`"
14
+ :class="{'VcTab': true, 'VcTabDisabled': item.disabled}"
15
+ active-class="VcTabActive"
16
+ :disabled="item.disabled"
17
+ :data-qa="`${dataQa}-tab-${index}`" >
18
+ <slot :name="`tab-${index}`" :item="item">
19
+ <span class="VcTab-label">{{ item.label }}</span>
20
+ </slot>
21
+ </v-tab>
22
+ </v-tabs>
23
+
24
+ <v-tabs-items :value="safeCurrentIndex" class="VcTabsContent">
25
+ <v-tab-item v-for="(item, index) in tabs" :key="`tab-content-${index}`" :data-qa="`${dataQa}-tab-content-${index}`">
26
+ <slot :name="`tab-content-${index}`" :item="item">
27
+ <div>{{item.label}}</div>
28
+ </slot>
29
+ </v-tab-item>
30
+ </v-tabs-items>
31
+ </div>
32
+ </template>
33
+
34
+ <script>
35
+ export default {
36
+ name: "VcTabs",
37
+ model: {
38
+ prop: 'currentIndex',
39
+ event: 'change',
40
+ },
41
+ props: {
42
+ tabs: {
43
+ type: Array,
44
+ required: true,
45
+ validator: value => value.every(tab => tab.label)
46
+ },
47
+ tabSize: {
48
+ type: String,
49
+ default: 'M',
50
+ validator: value => ['M', 'L'].includes(value)
51
+ },
52
+ currentIndex: {
53
+ type: Number,
54
+ default: 0,
55
+ validator: prop => prop >= 0,
56
+ },
57
+ distribution: {
58
+ type: String,
59
+ default: "start",
60
+ validator: prop => ['start','equal', 'grow'].includes(prop)
61
+ },
62
+ separatorStyle: {
63
+ type: String,
64
+ default: 'default',
65
+ validator: prop => ['default','shadow', 'divider'].includes(prop)
66
+ },
67
+ dataQa: {
68
+ type: String,
69
+ default: 'VcTabs'
70
+ },
71
+ },
72
+ computed: {
73
+ safeCurrentIndex() {
74
+ return Math.max(0, Math.min(this.currentIndex, this.tabs.length - 1));
75
+ }
76
+ }
77
+ }
78
+ </script>
79
+
80
+ <style lang="scss" scoped>
81
+
82
+ .VcTabs--wrapper {
83
+
84
+ .separatorShadow {
85
+ border-bottom: 1px solid var(--gray-lighten-1);
86
+ box-shadow: var(--shadow-1);
87
+ }
88
+
89
+ .separatorDivider {
90
+ border-bottom: 1px solid var(--gray-lighten-1);
91
+ }
92
+ }
93
+
94
+ .VcTab:focus {
95
+ color: rgba(255,255,255,0) !important; // The tabs should take the color of what's behind them
96
+ box-shadow: inset 0 0 0 3px var(--v-secondary-lighten1);
97
+ border-radius: 2px;
98
+
99
+ .VcTab-label {
100
+ color: var(--gray-darken-4);
101
+ }
102
+
103
+ &.VcTabDisabled {
104
+ background: rgba(0, 0, 0, 0.04);
105
+ color: #999999;
106
+ }
107
+ }
108
+
109
+ .VcTabActive {
110
+ color: var(--gray-darken-5);
111
+ }
112
+
113
+ .VcTab-label {
114
+ font-size: var(--font-size-x-small);
115
+ font-weight: var(--font-weight-medium2);
116
+ line-height: var(--size-value4);
117
+ letter-spacing: 0px;
118
+ text-transform: initial;
119
+ }
120
+
121
+ .VcTabsContent {
122
+ background-color: transparent;
123
+ }
124
+
125
+ </style>
@@ -89,6 +89,14 @@ dialogPlayground.args = {
89
89
  description: `Upsell block - dialog view. Pass mode=dialog to VcUpsellBlock component and wrap it with VcModalContainer. \nIf 'responsive' size is used and width/height changes are needed - you may change the css of VcModalContainer according to your development needs.`
90
90
  }
91
91
 
92
+ dialogPlayground.parameters = {
93
+ docs: {
94
+ source: {
95
+ type: 'code'
96
+ }
97
+ }
98
+ }
99
+
92
100
  export const dialogWithoutBulletsIcon = dialogTemplate.bind({});
93
101
 
94
102
  dialogWithoutBulletsIcon.args = {
@@ -104,6 +112,14 @@ dialogWithoutBulletsIcon.args = {
104
112
  description: `Upsell block - dialog view. Icon is optional.`
105
113
  }
106
114
 
115
+ dialogWithoutBulletsIcon.parameters = {
116
+ docs: {
117
+ source: {
118
+ type: 'code'
119
+ }
120
+ }
121
+ }
122
+
107
123
  export default {
108
124
  title: ' Content Display / VcUpsellBlock', // This will control the story sidebar position
109
125
  id: 'VcUpsellBlock', // This will be the permanent link to this component
@@ -120,5 +136,13 @@ export default {
120
136
  type: 'stable', // 'beta' | 'stable' | 'deprecated' | 'releaseCandidate'
121
137
  url: 'www.vcita.com', // will make the tag a link
122
138
  },
139
+ docs: {
140
+ source: {
141
+ // code: 'Your code snippet goes here.',
142
+ // format: false,
143
+ language: "html",
144
+ // type: "auto",
145
+ },
146
+ },
123
147
  },
124
148
  };
@@ -62,3 +62,4 @@ export {default as VcDocItem} from './VcDocItem/VcDocItem.vue';
62
62
  export {default as VcTimeSince} from './VcTimeSince/VcTimeSince.vue';
63
63
  export {default as VcSearchPicker} from './VcSearchPicker/VcSearchPicker.vue';
64
64
  export {default as VcDraggableList} from './VcDraggableList/VcDraggableList.vue';
65
+ export {default as VcTabs} from './VcTabs/VcTabs.vue';
@@ -10,7 +10,7 @@ const GeneralTemplate = (args, {argTypes}) => ({
10
10
  template: `
11
11
  <div>
12
12
  <VcWizard
13
- v-model="currentStep"
13
+ v-model.number="currentStep"
14
14
  :steps="steps"
15
15
  @submit="onSubmit"
16
16
  :size="size"
@@ -95,7 +95,7 @@ Playground.args = {
95
95
  },
96
96
  ],
97
97
  dataQa: 'vc-wizard',
98
- size: 'md',
98
+ size: 'md'
99
99
  }
100
100
 
101
101
  export default {
@@ -233,9 +233,3 @@ variables for `Dropzone`:
233
233
  Variables for `VcSelectField`:
234
234
 
235
235
  <div><code>--select-field-height: 52px;</code></div>
236
-
237
-
238
- variables for `VcSearchPicker`:
239
-
240
- <div><code>--search-picker-border-color: #1e93b7;</code></div>
241
- <div><code>--search-picker-second-border-color: #95cddb;</code></div>
@@ -60,10 +60,6 @@
60
60
  // Select field height
61
61
  --select-field-height: 52px;
62
62
 
63
- // --search-picker
64
- --search-picker-border-color: #1e93b7;
65
- --search-picker-second-border-color: #95cddb;
66
-
67
63
  // Colors
68
64
  // Gray
69
65
  --gray-darken-5: #212121; /*rgba(0, 0, 0, 0.87)*/