@worksafevictoria/wcl7.5 1.1.0 → 1.1.2

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.
@@ -1,47 +1,59 @@
1
1
  <template>
2
- <div class="wcl-search">
3
- <b-input-group>
4
- <label class="visually-hidden" for="site-search"
5
- >Search by keyword (Typed keyword automatically filters below
6
- results)</label
7
- >
8
- <b-form-input
9
- id="site-search"
10
- v-model="searchQuery"
11
- aria-label="searchbar"
12
- debounce="600"
13
- autocomplete="off"
14
- trim
15
- @update="onChange"
16
- ></b-form-input>
17
- <b-input-group-append>
18
- <b-button size="sm" @click="onSearch"
19
- ><span class="not-extra-small-screen">Search</span>
20
- <img alt="search icon" :src="searchIcon"
21
- /></b-button>
22
- </b-input-group-append>
23
- </b-input-group>
24
- <search-listing
25
- v-if="searchResults || isLoading"
26
- :class="{
27
- [`wcl-search__typeahead`]: isTypeahead,
28
- [`wcl-search__modal`]: isTypeahead
29
- }"
30
- :is-loading="isLoading"
31
- :is-typeahead="isTypeahead"
32
- :num-found="searchResults && searchResults.numFound"
33
- :query="searchResults && searchResults.query"
34
- :results="searchResults ? searchResults.results : []"
35
- :offset="searchResults && searchResults.offset"
36
- :page-limit="pageLimit"
37
- :content-parser="contentParser"
38
- @selected="$emit('selected')"
39
- @pageChanged="pageChanged"
40
- />
2
+ <div :class="searchClass">
3
+ <template v-if="searchType === 'google'">
4
+ <div class="wcl-google">
5
+ <div class="gcse-search"></div>
6
+ </div>
7
+ </template>
8
+ <template v-else>
9
+ <b-input-group>
10
+ <label class="visually-hidden" for="site-search"
11
+ >Search by keyword (Typed keyword automatically filters below
12
+ results)</label
13
+ >
14
+ <b-form-input
15
+ id="site-search"
16
+ v-model="searchQuery"
17
+ name="search"
18
+ aria-label="searchbar"
19
+ debounce="600"
20
+ autocomplete="off"
21
+ trim
22
+ @update="searchType !== 'googlerest' ? onChange : () => {}"
23
+ @keyup.enter="onChange"
24
+ ></b-form-input>
25
+ <b-input-group-append>
26
+ <b-button size="sm" class="search-button" @click="onSearch"
27
+ ><span class="not-extra-small-screen">Search</span>
28
+ <img alt="search icon" :src="searchIcon"
29
+ /></b-button>
30
+ </b-input-group-append>
31
+ </b-input-group>
32
+ <search-listing
33
+ v-if="loadSearchList && (searchResults || isLoading)"
34
+ :class="{
35
+ [`wcl-search__typeahead`]: isTypeahead,
36
+ [`wcl-search__modal`]: isTypeahead
37
+ }"
38
+ :is-loading="isLoading"
39
+ :is-typeahead="isTypeahead"
40
+ :num-found="searchResults && searchResults.numFound"
41
+ :suggestion="searchResults && searchResults.suggestion"
42
+ :query="searchResults && searchResults.query"
43
+ :results="searchResults ? searchResults.results : []"
44
+ :offset="searchResults && searchResults.offset"
45
+ :page-limit="pageLimit"
46
+ :content-parser="contentParser"
47
+ :is-card-title-selectable="searchType === 'googlerest' ? true : false"
48
+ @selected="$emit('selected')"
49
+ @pageChanged="pageChanged"
50
+ />
51
+ </template>
41
52
  </div>
42
53
  </template>
43
54
 
44
55
  <script>
56
+ import axios from 'axios'
45
57
  import searchIcon from '../../../assets/icons/search.svg?url'
46
58
  import SearchListing from './SearchListing/index.vue'
47
59
  import { BButton, BInputGroup, BFormInput, BInputGroupAppend } from 'bootstrap-vue-next'
@@ -64,6 +76,14 @@ export default {
64
76
  initialSearchQuery: {
65
77
  type: String,
66
78
  default: ''
79
+ },
80
+ googleSearchFlag: {
81
+ type: String,
82
+ default: 'solar'
83
+ },
84
+ visibleSearchList: {
85
+ type: Boolean,
86
+ default: true
67
87
  }
68
88
  },
69
89
  data() {
@@ -72,28 +92,177 @@ export default {
72
92
  searchQuery: '',
73
93
  searchResults: null,
74
94
  isLoading: false,
75
- field_language: '8671'
95
+ field_language: '8671',
96
+ searchType: this.googleSearchFlag,
97
+ googleSearchScript: false,
98
+ loadSearchList: this.visibleSearchList
76
99
  }
77
100
  },
78
- mounted() {
79
- if (this.initialSearchQuery) {
80
- this.searchQuery = this.initialSearchQuery
81
- this.onSearch()
101
+ computed: {
102
+ searchClass() {
103
+ return {
104
+ 'wcl-search': true,
105
+ 'wcl-search__google': this.searchType !== 'solar'
106
+ }
82
107
  }
83
- if (this.isTypeahead && window) {
84
- window.document.addEventListener('click', this.closeSearchResults)
108
+ },
109
+ mounted() {
110
+ if (this.searchType === 'google') {
111
+ this.setupGoogleStyle()
112
+ } else {
113
+ if (this.initialSearchQuery) {
114
+ this.searchQuery = this.initialSearchQuery
115
+ this.onSearch()
116
+ }
117
+ if (this.isTypeahead && window) {
118
+ window.document.addEventListener('click', this.closeSearchResults)
119
+ }
85
120
  }
86
121
  },
87
122
  beforeDestroy() {
88
- if (this.isTypeahead && window) {
123
+ if (this.isTypeahead && window && document) {
89
124
  window.document.removeEventListener('click', this.closeSearchResults)
90
125
  }
91
126
  },
92
127
  methods: {
93
- onSearch() {
94
- this.resetSearchResults()
95
- this.$emit('query', this.searchQuery)
96
- this.performSearch(1, this.searchQuery, this.pageLimit)
128
+ async fetchSearchResults(apiKey, searchEngineId, query, start) {
129
+ try {
130
+ const response = await (this?.$axios || axios).get(
131
+ `https://www.googleapis.com/customsearch/v1`,
132
+ {
133
+ params: {
134
+ key: apiKey,
135
+ cx: searchEngineId,
136
+ q: query,
137
+ start: start
138
+ }
139
+ }
140
+ )
141
+ return response
142
+ } catch (error) {
143
+ console.error('Error fetching search results:', error)
144
+ return []
145
+ }
146
+ },
147
+ async onGoogleSearch(pageNr = 1, searchQuery) {
148
+ this.$emit('loading', true)
149
+ this.isLoading = true
150
+
151
+ const apiKey = 'AIzaSyD76GAmQQ6DuxKWf-aLXPZ9pwdz4nOvs2c'
152
+ const searchEngineId = '53b1506aa03c64160'
153
+
154
+ const start = (pageNr - 1) * this.pageLimit
155
+ let response = await this.fetchSearchResults(
156
+ apiKey,
157
+ searchEngineId,
158
+ searchQuery,
159
+ start
160
+ )
161
+ let items = []
162
+ let suggest = response?.data?.spelling?.correctedQuery || ''
163
+ if (response?.data?.spelling?.correctedQuery) {
164
+ response = await this.fetchSearchResults(
165
+ apiKey,
166
+ searchEngineId,
167
+ response?.data?.spelling?.correctedQuery,
168
+ start
169
+ )
170
+ items = response?.data?.items
171
+ } else if (response?.data?.items) {
172
+ items = response?.data?.items
173
+ }
174
+ if (items.length > 0) {
175
+ const totalResults = response?.data?.searchInformation?.totalResults
176
+ // Loop through each item in the items array
177
+ const modifiedResults = items.map((item) => {
178
+ // Update the title using the handleSearchResultLinkTitle function
179
+ const modifiedTitle = this.handleSearchResultLinkTitle(item.title)
180
+
181
+ // Return the modified item with the updated title
182
+ return {
183
+ ...item,
184
+ title: modifiedTitle
185
+ }
186
+ })
187
+
188
+ this.searchResults = {
189
+ offset: Number(pageNr === 1 ? pageNr - 1 : pageNr),
190
+ numFound: Number(totalResults),
191
+ query: this.searchQuery,
192
+ results: modifiedResults,
193
+ suggestion: suggest
194
+ }
195
+ } else {
196
+ this.searchResults = {
197
+ offset: 0,
198
+ numFound: 0,
199
+ query: this.searchQuery ?? '',
200
+ results: [],
201
+ suggestion: suggest
202
+ }
203
+ }
204
+ this.$emit('loading', false)
205
+ this.isLoading = false
206
+ this.$emit('results', this.searchResults)
207
+
208
+ // wait for the page title to be updated and then fire the search event
209
+ setTimeout(() => {
210
+ const attrs = {
211
+ pageTitle: document.title,
212
+ pageURL: this.$route?.fullPath,
213
+ location:
214
+ this.$route?.path === '/'
215
+ ? 'Homepage'
216
+ : this.$route?.path === '/search'
217
+ ? 'Search Page'
218
+ : this.$route?.path,
219
+ label: this.searchQuery,
220
+ results: this.searchResults.numFound
221
+ }
222
+ if (this.$gtm) {
223
+ this.$gtm.push({ event: 'custom.search.site.submit', ...attrs })
224
+ }
225
+ }, 300)
226
+ },
227
+ pageChanged(newPageNumber) {
228
+ if (this.searchType === 'solar') {
229
+ // Reset search results before fetching new results
230
+ this.resetSearchResults()
231
+ this.performSearch(newPageNumber, this.searchQuery, this.pageLimit)
232
+ } else {
233
+ // Fetch new search results with the updated page number
234
+ this.onGoogleSearch(newPageNumber, this.searchQuery)
235
+ }
236
+ },
237
+ onSearch(e) {
238
+ const { path } = this.$route || {}
239
+
240
+ // Home page
241
+ if (
242
+ (e?.type === 'click' || e?.key === 'Enter') &&
243
+ this.searchQuery &&
244
+ this.searchQuery.length > 2 &&
245
+ path === '/'
246
+ ) {
247
+ // Search page
248
+ const searchQuery = encodeURIComponent(this.searchQuery)
249
+ const origin = window.location.origin
250
+ const hash = window.location.hash
251
+ const pathname = 'search'
252
+ const URL = `${origin}/${pathname}${hash}?q=${searchQuery}`
253
+
254
+ // Uncomment below commented code when goes live
255
+ window.location.assign(URL)
256
+ } else {
257
+ this.resetSearchResults()
258
+ this.$emit('query', this.searchQuery)
259
+
260
+ if (this.searchType === 'solar') {
261
+ this.performSearch(1, this.searchQuery, this.pageLimit)
262
+ } else {
263
+ this.onGoogleSearch(1, this.searchQuery)
264
+ }
265
+ }
97
266
  },
98
267
  resetSearchResults() {
99
268
  this.searchResults = null
@@ -109,15 +278,11 @@ export default {
109
278
  this.$emit('query', null)
110
279
  }
111
280
  },
112
- onChange() {
281
+ onChange(e) {
113
282
  if (this.searchQuery && this.searchQuery.length > 2) {
114
- this.onSearch()
283
+ this.onSearch(e)
115
284
  }
116
285
  },
117
- pageChanged(newPageNumber) {
118
- this.resetSearchResults()
119
- this.performSearch(newPageNumber, this.searchQuery, this.pageLimit)
120
- },
121
286
  performSearch(pageNr = 1, searchQuery, pageLimit) {
122
287
  if (searchQuery) {
123
288
  this.$emit('loading', true)
@@ -133,14 +298,16 @@ export default {
133
298
  offset: Number(res.offset),
134
299
  numFound: Number(res.numFound),
135
300
  query: searchQuery,
136
- results: res.results
301
+ results: res.results,
302
+ suggestion: null
137
303
  }
138
304
  } else {
139
305
  this.searchResults = {
140
306
  offset: 0,
141
307
  numFound: 0,
142
308
  query: this.searchQuery ?? '',
143
- results: []
309
+ results: [],
310
+ suggestion: null
144
311
  }
145
312
  }
146
313
  this.$emit('loading', false)
@@ -166,6 +333,59 @@ export default {
166
333
  }, 300)
167
334
  })
168
335
  }
336
+ },
337
+
338
+ // Method to customize google search style
339
+ setupGoogleStyle() {
340
+ if (this.googleSearchScript === false) {
341
+ const script = document.createElement('script')
342
+ script.async = true
343
+ script.src = process.env.GOOGLE__URL
344
+ document.head.appendChild(script)
345
+ this.googleSearchScript = true
346
+
347
+ // Create a new style element
348
+ const style = document.createElement('style')
349
+ style.id = 'searchStyle' // Assign an ID to the style element
350
+ // The CSS we are going to inject
351
+ const cssVar = 'table.gssb_c {display: none !important;}'
352
+ // Inject the style element into the head
353
+ document.head.appendChild(style)
354
+ // Set the text content of the style element to the CSS text
355
+ style.textContent = cssVar
356
+ }
357
+ },
358
+
359
+ // function to handle search result link titles
360
+ handleSearchResultLinkTitle(title) {
361
+ // Split the title by the '|' character
362
+ const titleParts = title.split('|')
363
+
364
+ // Remove any leading or trailing whitespace from each part
365
+ const cleanedTitleParts = titleParts.map((part) => part.trim())
366
+
367
+ // Join the cleaned title parts back together without the text after the '|' character
368
+ const modifiedTitle = cleanedTitleParts[0] // Take only the first part before '|'
369
+
370
+ return modifiedTitle
371
+ },
372
+
373
+ handleKeyUp(event) {
374
+ if (event.key === 'Enter' || event.type === 'click') {
375
+ let keyword = event.key
376
+ ? event.target.value
377
+ : document.querySelector('input[name="search"]').value
378
+
379
+ // Check if the URL does not contain "/search"
380
+ if (keyword && !window.location.href.includes('/search')) {
381
+ let origin = window.location.origin
382
+ let hash = window.location.hash
383
+ let pathname = 'search'
384
+ let URL = origin + '/' + pathname + hash
385
+ }
386
+
387
+ this.setupPaginationEvent()
388
+ }
169
389
  }
170
390
  }
171
391
  }
@@ -234,5 +454,289 @@ export default {
234
454
  display: none;
235
455
  }
236
456
  }
457
+ // Google Search styling
458
+ &.wcl-search__google {
459
+ :deep(form.gsc-search-box) {
460
+ position: relative;
461
+ display: -ms-flexbox;
462
+ display: flex;
463
+ -ms-flex-wrap: wrap;
464
+ flex-wrap: wrap;
465
+ -ms-flex-align: stretch;
466
+ align-items: stretch;
467
+ width: 100%;
468
+ }
469
+
470
+ :deep(.gsib_a) {
471
+ padding: 0 !important;
472
+ height: 56px;
473
+ }
474
+
475
+ :deep(.gsib_b) {
476
+ position: absolute;
477
+ right: 5px;
478
+ top: 15px;
479
+ }
480
+
481
+ :deep(.gcsc-more-maybe-branding-root) {
482
+ display: none;
483
+ }
484
+
485
+ :deep(table.gsc-input) {
486
+ position: relative;
487
+ }
488
+
489
+ :deep(td.gsc-input) {
490
+ border: none;
491
+ padding: 0;
492
+ width: 100%;
493
+ }
494
+
495
+ :deep(.gsc-input-box) {
496
+ border: none;
497
+ }
498
+
499
+ :deep(input.gsc-input) {
500
+ border-radius: 8px 0px 0px 8px;
501
+ border: 1px solid $gray !important;
502
+ border-right: 0 !important;
503
+ background: none !important;
504
+ color: #495057;
505
+ height: 56px !important;
506
+ position: relative;
507
+ -ms-flex: 1 1 auto;
508
+ flex: 1 1 auto;
509
+ padding: 0.375rem 0.75rem !important;
510
+
511
+ @media screen and (max-width: 320px) {
512
+ width: 220px;
513
+ }
514
+
515
+ &:focus {
516
+ box-shadow: none;
517
+ }
518
+ }
519
+
520
+ :deep(.gsc-search-button) {
521
+ display: -ms-flexbox;
522
+ display: flex;
523
+ margin-left: 0px;
524
+ height: 56px;
525
+ margin-top: 0px;
526
+ }
527
+
528
+ :deep(button.gsc-search-button) {
529
+ background: $orange;
530
+ border: none;
531
+ border-radius: 0px 8px 8px 0px;
532
+ color: $black;
533
+ display: flex;
534
+ align-items: center;
535
+ height: 56px !important;
536
+ font-size: 16px;
537
+ font-weight: 700;
538
+ padding-right: 32px;
539
+ padding-left: 32px;
540
+
541
+ &:before {
542
+ content: 'Search';
543
+ }
544
+
545
+ @media screen and (max-width: 767px) {
546
+ margin-top: 6px;
547
+ }
548
+
549
+ @media screen and (max-width: 320px) {
550
+ padding-right: 16px;
551
+ padding-left: 16px;
552
+ }
553
+
554
+ svg {
555
+ fill: black;
556
+ height: 18px;
557
+ width: 18px;
558
+ margin-left: 10px;
559
+ }
560
+
561
+ :focus {
562
+ box-shadow: 0px;
563
+ }
564
+ }
565
+
566
+ :deep(.gsst_a .gscb_a) {
567
+ color: #191919;
568
+ }
569
+
570
+ :deep(.gsc-result-info) {
571
+ color: #191919;
572
+ font-size: 16px;
573
+ font-weight: 400;
574
+ }
575
+
576
+ :deep(.gsc-above-wrapper-area) {
577
+ padding: 20px 0;
578
+ border-bottom: none;
579
+ }
580
+
581
+ /* search list start */
582
+ :deep(.gsc-url-top) {
583
+ display: none;
584
+ }
585
+
586
+ :deep(.gsc-table-result) {
587
+ line-height: 1.6;
588
+ padding: 0 5px;
589
+ font-size: 16px;
590
+ font-weight: 400;
591
+ width: 80%;
592
+
593
+ b {
594
+ color: #191919;
595
+ font-size: 16px;
596
+ font-weight: normal;
597
+ }
598
+ }
599
+
600
+ :deep(.gsc-resultsRoot.gsc-tabData) {
601
+ width: 100%;
602
+ margin-left: auto;
603
+ margin-right: auto;
604
+ padding-left: 15px;
605
+ padding-right: 15px;
606
+
607
+ &:hover {
608
+ border: none;
609
+ }
610
+ }
611
+
612
+ :deep(.gs-bidi-start-align.gs-snippet) {
613
+ color: #191919 !important;
614
+ line-height: 24px;
615
+ }
616
+
617
+ :deep(.gsc-thumbnail-inside) {
618
+ position: relative;
619
+ padding: 0;
620
+
621
+ &::after {
622
+ content: ' ';
623
+ position: absolute;
624
+ height: 16px;
625
+ width: 11px;
626
+ top: 5px;
627
+ right: 16px;
628
+ background-repeat: no-repeat;
629
+ background-image: url(./../../../assets/icons/chev-right.svg);
630
+ background-size: 100%;
631
+ }
632
+
633
+ div.gs-title {
634
+ width: 90%;
635
+ margin-bottom: 12px;
636
+ overflow: visible;
637
+ }
638
+
639
+ a.gs-title {
640
+ color: #191919 !important;
641
+ font-weight: 700;
642
+ line-height: 24px;
643
+ font-size: 20px;
644
+ border: 3px solid transparent;
645
+ padding-left: 5px;
646
+ padding-right: 5px;
647
+ text-decoration: underline;
648
+
649
+ &:hover {
650
+ border: 3px solid #da47ff;
651
+ color: #006bff !important;
652
+
653
+ b {
654
+ color: #006bff !important;
655
+ }
656
+ }
657
+
658
+ b {
659
+ color: #191919 !important;
660
+ font-size: 20px;
661
+ }
662
+ }
663
+ }
664
+
665
+ :deep(.gsc-orderby-label) {
666
+ font-size: 16px;
667
+ font-weight: 700;
668
+ color: #191919;
669
+ }
670
+
671
+ :deep(.gsc-selected-option-container) {
672
+ background-color: white;
673
+ border-radius: 10px;
674
+ margin-top: 0px;
675
+ padding-top: 6px;
676
+ margin-right: 0;
677
+ margin-left: 4px;
678
+ padding-left: 20px;
679
+ padding-bottom: 6px;
680
+ height: 40px;
681
+
682
+ .gsc-selected-option {
683
+ font-size: 16px;
684
+ font-weight: 400;
685
+ line-height: 24px;
686
+ margin-right: 20px;
687
+ }
688
+ }
689
+
690
+ :deep(.gsc-control-cse .gsc-option-selector) {
691
+ margin-top: 0;
692
+ margin-right: 10px;
693
+ }
694
+
695
+ :deep(.gsc-expansionArea > .gsc-webResult.gsc-result) {
696
+ border: 0;
697
+ flex: 0 0 100%;
698
+ max-width: 100%;
699
+ padding: 0;
700
+
701
+ &:first-child {
702
+ border-top: 1px solid #bababa;
703
+ }
704
+
705
+ > div {
706
+ border-bottom: 1px solid #bababa;
707
+ padding: 32px 0;
708
+ }
709
+ }
710
+
711
+ :deep(.gsc-results .gsc-cursor-box .gsc-cursor-current-page) {
712
+ background-color: lightgrey;
713
+ }
714
+
715
+ :deep(.gsc-results .gsc-cursor-box .gsc-cursor-page) {
716
+ border: 1px solid grey;
717
+ color: #333333;
718
+ padding: 10px 12px;
719
+ border-radius: 5px;
720
+ font-size: 16px;
721
+ font-weight: 400;
722
+ }
723
+
724
+ :deep(.gsc-results .gsc-cursor-box) {
725
+ margin-top: 50px;
726
+ }
727
+
728
+ /* search list end */
729
+ @include mq('xs') {
730
+ .not-extra-small-screen {
731
+ display: none;
732
+ }
733
+ }
734
+ }
735
+ }
736
+
737
+ @media screen and (max-width: 767px) {
738
+ :deep(.gssb_c) {
739
+ width: 85% !important;
740
+ }
237
741
  }
238
742
  </style>
@@ -1,8 +0,0 @@
1
- import GoogleSearch from './index.vue'
2
-
3
- export default {
4
- title: 'SubComponents/GoogleSearch',
5
- component: GoogleSearch
6
- }
7
-
8
- export const Default = {}