@iamproperty/components 2.9.0 → 3.1.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.
Files changed (89) hide show
  1. package/README.md +9 -130
  2. package/assets/css/core.min.css +1 -1
  3. package/assets/css/core.min.css.map +1 -1
  4. package/assets/css/style.min.css +1 -1
  5. package/assets/css/style.min.css.map +1 -1
  6. package/assets/favicons/manifest.json +32 -0
  7. package/assets/js/main.js +57 -0
  8. package/assets/js/modules/chart.js +18 -17
  9. package/assets/js/modules/file-upload.js +48 -0
  10. package/assets/js/modules/form.js +27 -18
  11. package/assets/js/modules/nav.js +9 -8
  12. package/assets/js/modules/orderablelist.js +122 -0
  13. package/assets/js/modules/table.js +24 -24
  14. package/assets/js/scripts.bundle.js +63 -52
  15. package/assets/js/scripts.bundle.js.map +1 -1
  16. package/assets/js/scripts.bundle.min.js +2 -2
  17. package/assets/js/scripts.bundle.min.js.map +1 -1
  18. package/assets/sass/_functions/utilities.scss +3 -110
  19. package/assets/sass/_functions/variables.scss +3 -2
  20. package/assets/sass/_tests/colours.spec.scss +45 -0
  21. package/assets/sass/_tests/func.spec.scss +233 -0
  22. package/assets/sass/_tests/mixins.spec.scss +194 -0
  23. package/assets/sass/_tests/sass.spec.js +9 -0
  24. package/assets/sass/_tests/typography.spec.scss +36 -0
  25. package/assets/sass/components/alert.scss +1 -1
  26. package/assets/sass/components/cardDeck.scss +1 -1
  27. package/assets/sass/components/carousel.scss +5 -5
  28. package/assets/sass/components/charts.scss +1 -1
  29. package/assets/sass/components/nav.scss +47 -2
  30. package/assets/sass/components/stepper.scss +3 -3
  31. package/assets/sass/elements/buttons.scss +46 -4
  32. package/assets/sass/elements/card.scss +112 -0
  33. package/assets/sass/elements/container.scss +13 -2
  34. package/assets/sass/elements/forms.scss +68 -0
  35. package/assets/sass/elements/links.scss +2 -1
  36. package/assets/sass/elements/lists.scss +48 -1
  37. package/assets/sass/elements/panel.scss +3 -3
  38. package/assets/sass/elements/tables.scss +1 -1
  39. package/assets/sass/elements/tooltips.scss +1 -1
  40. package/assets/sass/foundations/icons.scss +8 -0
  41. package/assets/sass/foundations/root.scss +23 -4
  42. package/assets/ts/main.js +57 -0
  43. package/assets/ts/main.js.map +1 -0
  44. package/assets/ts/main.ts +10 -10
  45. package/assets/ts/modules/accordion.js +33 -0
  46. package/assets/ts/modules/accordion.js.map +1 -0
  47. package/dist/components.es.js +2518 -0
  48. package/dist/components.umd.js +56 -3784
  49. package/dist/style.css +1 -0
  50. package/package.json +87 -87
  51. package/src/components/Accordion/Accordion.screenshot.vue +57 -0
  52. package/src/components/Accordion/Accordion.spec.js +63 -0
  53. package/src/components/Accordion/Accordion.vue +1 -1
  54. package/src/components/Accordion/AccordionItem.vue +1 -1
  55. package/src/components/Accordion/__screenshots__/win32/laptop/Accordion.png +0 -0
  56. package/src/components/Accordion/__screenshots__/win32/mobile/Accordion.png +0 -0
  57. package/src/components/Accordion/__screenshots__/win32/tablet/Accordion.png +0 -0
  58. package/src/components/Alert/Alert.spec.js +49 -0
  59. package/src/components/Alert/Alert.vue +3 -3
  60. package/src/components/Banner/Banner.spec.js +28 -0
  61. package/src/components/CardDeck/CardDeck.spec.js +99 -0
  62. package/src/components/CardDeck/CardDeck.vue +1 -1
  63. package/src/components/Carousel/Carousel.spec.js +45 -0
  64. package/src/components/Chart/Chart.spec.js +201 -0
  65. package/src/components/Chart/Chart.vue +3 -3
  66. package/src/components/Header/Header.spec.js +33 -0
  67. package/src/components/Modal/Modal.spec.js +22 -0
  68. package/src/components/Nav/Nav.spec.js +35 -0
  69. package/src/components/Nav/Nav.vue +5 -2
  70. package/src/components/Stepper/Stepper.spec.js +99 -0
  71. package/src/components/Tabs/Tab.vue +6 -0
  72. package/src/components/Tabs/Tabs.vue +15 -13
  73. package/src/components/Testimonial/Testimonial.spec.js +57 -0
  74. package/src/components/Timeline/Timeline.spec.js +17 -0
  75. package/src/elements/Card/Card.vue +11 -2
  76. package/src/elements/Input/Input.vue +14 -10
  77. package/src/elements/Table/Table.spec.js +90 -0
  78. package/src/elements/Table/Table.vue +8 -5
  79. package/src/foundations/Icon/Icon.spec.js +24 -0
  80. package/src/foundations/Logo/Logo.spec.js +56 -0
  81. package/src/vue-shim.d.ts +6 -0
  82. package/dist/components.common.js +0 -3773
  83. package/dist/components.common.js.map +0 -1
  84. package/dist/components.css +0 -2
  85. package/dist/components.css.map +0 -1
  86. package/dist/components.umd.js.map +0 -1
  87. package/dist/components.umd.min.js +0 -2
  88. package/dist/components.umd.min.js.map +0 -1
  89. package/dist/demo.html +0 -1
@@ -0,0 +1,57 @@
1
+ import { shallowMount } from '@vue/test-utils'
2
+ import Header from './Testimonial.vue'
3
+
4
+ describe('Testimonial component', () => {
5
+
6
+ const test = shallowMount(Header, {
7
+ propsData: {
8
+ items: [
9
+ {
10
+ cite: 'Name goes here',
11
+ quote: `<p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.</p>`,
12
+ image: '/img/src/img.png'
13
+ },
14
+ {
15
+ cite: 'Name goes here 2',
16
+ quote: `<p>It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English.</p>`,
17
+ image: '/img/src/img.png'
18
+ },
19
+ {
20
+ cite: 'Name goes here 3',
21
+ quote: `<p>It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.</p><p>There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable.</p>`,
22
+ class: 'largest',
23
+ image: '/img/src/img.png'
24
+ }
25
+ ]
26
+ },
27
+ slots: {
28
+ default: '<p>Hello</p>'
29
+ }
30
+ })
31
+
32
+ it('renders the testimonials title', () => {
33
+ expect(test.html()).toContain('<h2>What our customers think…</h2>')
34
+ })
35
+
36
+ it('renders the content after the quotes', () => {
37
+ expect(test.html()).toContain('<p>Hello</p>')
38
+ })
39
+
40
+ it('renders the correct number of blockquotes', () => {
41
+
42
+ expect(test.findAll('blockquote').length).toBe(3)
43
+ })
44
+
45
+ it('adds the largest class on the defined blockquote', () => {
46
+
47
+ const quotes = test.findAll('blockquote');
48
+ const largestQuote = quotes.at(2)
49
+
50
+ expect(largestQuote.classes()).toContain('largest')
51
+ })
52
+
53
+ it('adds the testimonial--multi class on load', () => {
54
+
55
+ expect(test.classes()).toContain('testimonial--multi')
56
+ })
57
+ })
@@ -0,0 +1,17 @@
1
+ import { shallowMount } from '@vue/test-utils'
2
+ import Header from './Timeline.vue'
3
+
4
+ describe('Timeline component', () => {
5
+
6
+ const test = shallowMount(Header, {
7
+ propsData: {
8
+ },
9
+ slots: {
10
+ default: '<p>Hello</p>'
11
+ }
12
+ })
13
+
14
+ it('renders the content after', () => {
15
+ expect(test.html()).toContain('<p>Hello</p>')
16
+ })
17
+ })
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <a :href="link" :class="'card'+(type?' card--'+type:'')" :title="'Find out more: '+title+(subtitle ? ' - '+subtitle:'')">
2
+ <a :href="link" :class="'card'+(type?' card--'+type:'')+' '+cardClass" :title="ctatext+': '+title+(subtitle ? ' - '+subtitle:'')" :target="target">
3
3
  <div class="card-header__wrapper" v-if="image">
4
4
  <img :src="image" alt="" loading="lazy" class="card-image" />
5
5
  <div class="card-header">
@@ -10,7 +10,7 @@
10
10
  </div>
11
11
  <div class="card-body" v-html="cardContent()"></div>
12
12
  <div class="card-footer" v-if="hidectatext == false">
13
- <span :class="`${btntype == 'link' ? `link` : `btn btn-${btntype}`} mb-0`">{{ctatext}}<span class="visually-hidden"> about {{title}}</span></span>
13
+ <span :class="`${btntype == 'link' ? `link` : `btn btn-${btntype}`} mb-0`">{{ctatext}}<span class="visually-hidden">: {{title}}</span></span>
14
14
  </div>
15
15
  </a>
16
16
  </template>
@@ -27,6 +27,11 @@ export default {
27
27
  type: String,
28
28
  required: false
29
29
  },
30
+ cardClass: {
31
+ type: String,
32
+ required: false,
33
+ default: ''
34
+ },
30
35
  titleclass: {
31
36
  type: String,
32
37
  required: false,
@@ -67,6 +72,10 @@ export default {
67
72
  type: String,
68
73
  required: false
69
74
  },
75
+ target: {
76
+ type: String,
77
+ required: false
78
+ },
70
79
  details: {
71
80
  type: Object,
72
81
  required: false
@@ -1,34 +1,34 @@
1
1
  <template>
2
2
  <div :class="wrapperClass()" ref="wrapper">
3
3
  <label v-if="needsLabel()" :class="`form-label${labelclass?` ${labelclass}`:''}`" :for="id" v-html="displayLabel()" :title="title"></label>
4
-
4
+
5
5
  <!-- Prefix and Suffix -->
6
6
  <span :class="`prefix ${this.prefixClass} ${size?`prefix-${size}`:''}`" v-html="prefix" v-if="prefix" role="presentation"></span>
7
7
  <span :class="`suffix ${this.suffixClass} ${size?`suffix-${size}`:''}`" v-html="suffix" v-if="suffix" role="presentation"></span>
8
8
 
9
9
  <!-- Standard input field -->
10
10
  <input v-if="isInput()" v-model="inputVal" :class="`form-control${size?` form-control-${size}`:``}${inputclass?` ${inputclass}`:``}`" :type="type" :name="name?name:id" :id="id" :pattern="needPattern()" :list="hasOptions()" v-bind="$attrs" v-on:keyup="inputKeyup" />
11
-
11
+
12
12
  <!-- Textarea -->
13
13
  <textarea v-if="type=='textarea'" v-model="inputVal" :class="`form-control${size?` form-control-${size}`:``}${inputclass?` ${inputclass}`:``}`" :type="type" :name="name?name:id" :id="id" :pattern="needPattern()" v-bind="$attrs"></textarea>
14
-
14
+
15
15
  <!-- Range -->
16
16
  <div class="input-group" v-if="type=='range'">
17
17
  <input v-model="inputVal" :class="`form-range${inputclass?` ${inputclass}`:``}`" :type="type" :name="name?name:id" :id="id" :pattern="needPattern()" :list="hasOptions()" v-bind="$attrs" oninput="this.nextElementSibling.value=this.value;" />
18
18
  <output class="input-group-text border-0 col-2 col-sm-1 px-0">{{value}}</output>
19
19
  </div>
20
-
20
+
21
21
  <!-- Color picker -->
22
22
  <div class="input-group" v-if="type=='color'">
23
23
  <input v-model="inputVal" :class="`form-control form-control-color${inputclass?` ${inputclass}`:``}`" :type="type" :name="name?name:id" :id="id" :pattern="needPattern()" :list="hasOptions()" v-bind="$attrs" oninput="this.nextElementSibling.value=this.value;" />
24
24
  <output class="input-group-text flex-fill">{{value?vale:'#000000'}}</output>
25
25
  </div>
26
-
26
+
27
27
  <!-- Select/dropdown -->
28
28
  <select v-if="type=='select'" v-model="inputVal" :class="`form-select${size?` form-select-${size}`:``}${inputclass?` ${inputclass}`:``}`" :type="type" :name="id" :id="id" :pattern="needPattern()" v-bind="$attrs">
29
29
  <option v-for="(value,index) in options" :key="index" :value="value.value">{{value.display ? value.display : value.value}}</option>
30
30
  </select>
31
-
31
+
32
32
  <datalist v-if="allowDatalist()" :id="id+'-list'">
33
33
  <option v-for="(value,index) in options" :key="index" :value="value.value">{{value.value}}</option>
34
34
  </datalist>
@@ -120,6 +120,10 @@ export default {
120
120
  hint: {
121
121
  type: String,
122
122
  required: false
123
+ },
124
+ multiple: {
125
+ type: String,
126
+ required: false
123
127
  }
124
128
  },
125
129
  computed: {
@@ -129,7 +133,7 @@ export default {
129
133
  if(this.$attrs.multiple){
130
134
  return this.label+`<span class="small d-block text-body font-body fw-normal">Hold down the Ctrl (windows) or Command (Mac) button to select multiple options.</span>`
131
135
  }
132
-
136
+
133
137
  if(this.hint){
134
138
  return this.label+`<span class="small d-block text-body font-body fw-normal">${this.hint}</span>`
135
139
  }
@@ -162,7 +166,7 @@ export default {
162
166
  case 'datetime-local':
163
167
  return '[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}'
164
168
  }
165
-
169
+
166
170
  return false
167
171
  }
168
172
  },
@@ -236,7 +240,7 @@ export default {
236
240
  return this.value;
237
241
  },
238
242
  set(val) {
239
-
243
+
240
244
  this.$emit('input', val);
241
245
  }
242
246
  }
@@ -244,7 +248,7 @@ export default {
244
248
  mounted(){
245
249
 
246
250
  this.$nextTick(function () {
247
-
251
+
248
252
  let element = this.$refs.wrapper;
249
253
  // Remove unnecessary divs that may get in the way of our CSS sibling selectors working
250
254
  if(element.parentNode && element.parentNode.classList.contains('form-check') || element.classList.length == 0){
@@ -0,0 +1,90 @@
1
+ import { mount } from '@vue/test-utils'
2
+ import Table from './Table.vue'
3
+
4
+ describe('Table component', () => {
5
+
6
+ const test = mount(Table, {
7
+ propsData: {
8
+ fields: [
9
+ { key: 'name', filterable: true, sortable: true },
10
+ { key: 'job', filterable: true, sortable: true },
11
+ { key: 'address' },
12
+ { key: 'emergency_contact', filterable: true, sortable: true },
13
+ { key: 'actions' }
14
+ ],
15
+ items: [
16
+ {
17
+ name: 'Derrick',
18
+ job: 'Electrician',
19
+ address: '5 King Street<br> London<br> London<br> SW20 0AL<br> United Kingdom',
20
+ emergency_contact: 'Susan',
21
+ actions: '<a href="#">View</a><br><a href="#">Point of Contact</a>'
22
+ },
23
+ {
24
+ name: 'Andrew',
25
+ job: 'Electrician',
26
+ address: '5 King Street<br> London<br> London<br> SW20 0AL<br> United Kingdom',
27
+ emergency_contact: 'Susan',
28
+ actions: '<a href="#">View</a><br><a href="#">Point of Contact</a>'
29
+ },
30
+ {
31
+ name: 'Rachel',
32
+ job: 'Electrician',
33
+ address: '5 King Street<br> London<br> London<br> SW20 0AL<br> United Kingdom',
34
+ emergency_contact: 'Susan',
35
+ actions: '<a href="#">View</a><br><a href="#">Point of Contact</a>'
36
+ }
37
+ ],
38
+ show: 2
39
+ }
40
+ })
41
+
42
+ // On load
43
+ it('renders a div with the class of table wrapper', () => {
44
+
45
+ expect(test.classes()).toContain('table__wrapper')
46
+ })
47
+
48
+ it('renders a thead', () => {
49
+
50
+ expect(test.find('thead').exists()).toBe(true)
51
+ })
52
+
53
+ it('renders a tbody', () => {
54
+
55
+ expect(test.find('tbody').exists()).toBe(true)
56
+ })
57
+
58
+ it('renders a filters form when needed', () => {
59
+
60
+ expect(test.find('.table__filters').exists()).toBe(true)
61
+ })
62
+
63
+ it('renders a pagination form when needed', () => {
64
+
65
+ expect(test.find('.table__pagination').exists()).toBe(true)
66
+ })
67
+
68
+ // Events
69
+ it('can be sorted by name', async () => {
70
+
71
+ const firstCol = test.find('[data-sortable]')
72
+ expect(firstCol.html()).toContain('Name')
73
+
74
+ await firstCol.trigger('click')
75
+
76
+ let firstRowCol = test.find('tbody tr:first-child [data-label="Name"]')
77
+ expect(firstRowCol.html()).toContain('Andrew')
78
+ })
79
+
80
+ it('can be filtered by name', async () => {
81
+
82
+ const searchField = test.find('[type="search"]')
83
+
84
+ await searchField.setValue('Andrew')
85
+ await searchField.trigger('change')
86
+
87
+ expect(test.findAll('tbody tr').length).toBe(1)
88
+ })
89
+
90
+ })
@@ -22,6 +22,9 @@ import table from '../../../assets/js/modules/table.js'
22
22
 
23
23
  let numericValue = function(value) {
24
24
 
25
+ if(typeof(value) != "string")
26
+ return value;
27
+
25
28
  value = value.replace('£','')
26
29
  value = value.replace('%','')
27
30
 
@@ -85,17 +88,17 @@ export default {
85
88
  mounted(){
86
89
 
87
90
  this.$nextTick(function () {
88
-
91
+
89
92
  table(this.$refs.wrapper);
90
93
 
91
94
  // Listen for the event.
92
- this.$el.addEventListener('sorted', function (e) {
93
-
95
+ this.$el.addEventListener('sorted', function (e) {
96
+
94
97
  console.log('Table sorted')
95
98
  }, false);
96
99
 
97
- this.$el.addEventListener('filtered', function (e) {
98
-
100
+ this.$el.addEventListener('filtered', function (e) {
101
+
99
102
  console.log('Table filtered')
100
103
  }, false);
101
104
 
@@ -0,0 +1,24 @@
1
+ import { shallowMount } from '@vue/test-utils'
2
+ import Icon from './Icon.vue'
3
+
4
+ describe('Icon component', () => {
5
+ it('renders the email Icon by default', () => {
6
+ const test = shallowMount(Icon, {
7
+ propsData: { }
8
+ })
9
+
10
+ expect(test.html()).toContain('<title>email</title>')
11
+ expect(test.html()).toContain('<use xlink:href="/svg/icons.svg#icon-email"></use>')
12
+ })
13
+
14
+ it('renders the correct logo when an ID is passed', () => {
15
+ const test = shallowMount(Icon, {
16
+ propsData: {
17
+ id: 'pin'
18
+ }
19
+ })
20
+
21
+ expect(test.html()).toContain('<title>pin</title>')
22
+ expect(test.html()).toContain('<use xlink:href="/svg/icons.svg#icon-pin"></use>')
23
+ })
24
+ })
@@ -0,0 +1,56 @@
1
+ import { shallowMount } from '@vue/test-utils'
2
+ import Logo from './Logo.vue'
3
+
4
+ describe('Logo component', () => {
5
+ it('renders the iam property logo by default', () => {
6
+ const test = shallowMount(Logo, {
7
+ propsData: { }
8
+ })
9
+
10
+ expect(test.html()).toContain('<title>iam property</title>')
11
+ expect(test.html()).toContain('<use xlink:href="/svg/logo.svg#logo-property"></use>')
12
+ })
13
+
14
+ it('renders the correct logo when an ID is passed', () => {
15
+ const test = shallowMount(Logo, {
16
+ propsData: {
17
+ id: 'sold'
18
+ }
19
+ })
20
+
21
+ expect(test.html()).toContain('<title>iam sold</title>')
22
+ expect(test.html()).toContain('<use xlink:href="/svg/logo.svg#logo-sold"></use>')
23
+ })
24
+
25
+ it('renders a description when passed', () => {
26
+ const test = shallowMount(Logo, {
27
+ propsData: { }
28
+ })
29
+
30
+ expect(test.html()).not.toContain('<span></span>')
31
+
32
+ const test2 = shallowMount(Logo, {
33
+ propsData: {
34
+ desc: 'description'
35
+ }
36
+ })
37
+
38
+ expect(test2.html()).toContain('<span>description</span>')
39
+ })
40
+
41
+ it('background can be modified', () => {
42
+ const test = shallowMount(Logo, {
43
+ propsData: { }
44
+ })
45
+
46
+ expect(test.html()).not.toContain('<span></span>')
47
+
48
+ const test2 = shallowMount(Logo, {
49
+ propsData: {
50
+ desc: 'description'
51
+ }
52
+ })
53
+
54
+ expect(test2.html()).toContain('<span>description</span>')
55
+ })
56
+ })
@@ -0,0 +1,6 @@
1
+ declare module "*.vue" {
2
+ import { DefineComponent } from 'vue'
3
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
4
+ const component: DefineComponent<{}, {}, any>
5
+ export default component
6
+ }