@windward/core 0.12.0 → 0.12.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ### Hotfix [0.12.2] created - 2025-02-21
4
+
5
+
6
+ ### Hotfix [0.12.1] created - 2025-02-21
7
+
8
+
3
9
  ### Release [0.12.0] created - 2025-02-18
4
10
 
5
11
 
@@ -83,6 +83,9 @@ export default {
83
83
  ImageAssetViewer,
84
84
  },
85
85
  extends: BaseContentBlock,
86
+ data() {
87
+ return {}
88
+ },
86
89
  beforeMount() {
87
90
  this.block.body = this.$t(
88
91
  'windward.core.shared.content_blocks.title.tab'
@@ -117,9 +120,6 @@ export default {
117
120
  this.block.metadata.config.currentTab = 0
118
121
  }
119
122
  },
120
- data() {
121
- return {}
122
- },
123
123
  methods: {
124
124
  async onBeforeSave() {
125
125
  this.block.metadata.config.items.forEach((element) => {
@@ -128,12 +128,15 @@ export default {
128
128
  this.block.metadata.config.currentTab = null
129
129
  },
130
130
  getImageWidth(item) {
131
- if (item.imageAsset?.display) {
131
+ if (item?.imageAsset?.display) {
132
132
  return String(item.imageAsset.display.width) + '%'
133
133
  }
134
134
  },
135
135
  getTextImageContainerClass(item) {
136
- const { margin, width } = item.imageAsset.display
136
+ const { margin, width } = item?.imageAsset?.display || {
137
+ margin: null,
138
+ width: null,
139
+ }
137
140
  // if full width set container to column
138
141
  if (width === 100) {
139
142
  return margin === 'top'
@@ -160,7 +163,7 @@ export default {
160
163
  }
161
164
  },
162
165
  getImageOrder(item) {
163
- const margin = item.imageAsset.display?.margin
166
+ const margin = item?.imageAsset?.display?.margin
164
167
  // filter out tab blocks that were created before custom
165
168
  // spacing was added to image asset settings
166
169
  if (margin) {
@@ -1,64 +1,65 @@
1
1
  <template>
2
- <div
3
- v-click-outside="onClickOutside"
4
- class="glossary-word"
5
- :class="{ active: show }"
6
- @click="show = !show"
7
- >
8
- <v-tooltip
2
+ <div class="glossary-word" :class="{ active: show }">
3
+ <v-menu
9
4
  v-model="show"
10
- top
11
- :open-on-hover="false"
12
- color="primary"
5
+ :close-on-content-click="false"
6
+ offset-y
13
7
  max-width="768px"
14
- z-index="10"
8
+ :z-index="20"
15
9
  >
16
10
  <template #activator="{ on }">
17
- <span tabindex="0" v-on="on">
11
+ <span
12
+ tabindex="0"
13
+ v-on="on"
14
+ @click.stop="show = !show"
15
+ >
18
16
  <slot name="term"></slot>
19
17
  </span>
20
18
  </template>
21
- <div>
22
- <div v-if="$slots['definition']">
23
- <h6 class="text-capitalize">
24
- <slot name="term"></slot>
25
- {{
26
- $t(
27
- 'windward.core.components.utils.tiny_mce_wrapper.definition'
28
- )
29
- }}
30
- :
31
- </h6>
32
- <p>
33
- <slot name="definition"></slot>
34
- </p>
35
- </div>
36
- <div v-if="$slots['alternate_forms']">
37
- <h4 class="text-capitalize">
38
- <slot name="term"></slot>
39
- {{
40
- $t(
41
- 'windward.core.components.utils.tiny_mce_wrapper.alternate_forms'
42
- )
43
- }}
44
- :
45
- </h4>
46
- <p><slot name="alternate_forms"></slot></p>
47
- </div>
48
19
 
49
- <div v-if="$slots['related_terms']">
50
- <h4 class="text-capitalize">
51
- {{
52
- $t(
53
- 'windward.core.components.utils.tiny_mce_wrapper.related_terms'
54
- )
55
- }}
56
- :
57
- </h4>
58
- <p><slot name="related_terms"></slot></p>
59
- </div>
60
- </div>
61
- </v-tooltip>
20
+ <v-card>
21
+ <v-card-text>
22
+ <div v-if="$slots['definition']">
23
+ <h6 class="text-capitalize">
24
+ <slot name="term"></slot>
25
+ {{
26
+ $t(
27
+ 'windward.core.components.utils.tiny_mce_wrapper.definition'
28
+ )
29
+ }}
30
+ :
31
+ </h6>
32
+ <p>
33
+ <slot name="definition"></slot>
34
+ </p>
35
+ </div>
36
+ <div v-if="$slots['alternate_forms']">
37
+ <h4 class="text-capitalize">
38
+ <slot name="term"></slot>
39
+ {{
40
+ $t(
41
+ 'windward.core.components.utils.tiny_mce_wrapper.alternate_forms'
42
+ )
43
+ }}
44
+ :
45
+ </h4>
46
+ <p><slot name="alternate_forms"></slot></p>
47
+ </div>
48
+
49
+ <div v-if="$slots['related_terms']">
50
+ <h4 class="text-capitalize">
51
+ {{
52
+ $t(
53
+ 'windward.core.components.utils.tiny_mce_wrapper.related_terms'
54
+ )
55
+ }}
56
+ :
57
+ </h4>
58
+ <p><slot name="related_terms"></slot></p>
59
+ </div>
60
+ </v-card-text>
61
+ </v-card>
62
+ </v-menu>
62
63
  </div>
63
64
  </template>
64
65
 
@@ -67,14 +68,12 @@ export default {
67
68
  name: 'GlossaryToolTip',
68
69
  data() {
69
70
  return {
70
- show: false,
71
+ show: false
71
72
  }
72
73
  },
73
- methods: {
74
- onClickOutside() {
75
- this.show = false
76
- },
77
- },
74
+ beforeDestroy() {
75
+ this.show = false
76
+ }
78
77
  }
79
78
  </script>
80
79
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windward/core",
3
- "version": "0.12.0",
3
+ "version": "0.12.2",
4
4
  "description": "Windward UI Core Plugins",
5
5
  "main": "plugin.js",
6
6
  "scripts": {
@@ -0,0 +1,136 @@
1
+ import { mount, createLocalVue } from '@vue/test-utils'
2
+ import Vue from 'vue'
3
+ import Vuetify from 'vuetify'
4
+ import VueI18n from 'vue-i18n'
5
+ import GlossaryToolTip from '../../../../components/utils/glossary/GlossaryToolTip.vue'
6
+
7
+ Vue.use(Vuetify)
8
+ Vue.use(VueI18n)
9
+
10
+ describe('GlossaryToolTip', () => {
11
+ const localVue = createLocalVue()
12
+ let vuetify
13
+ let i18n
14
+ let wrapper
15
+
16
+ beforeEach(() => {
17
+ vuetify = new Vuetify()
18
+ i18n = new VueI18n({
19
+ locale: 'en',
20
+ messages: {
21
+ en: {
22
+ 'windward.core.components.utils.tiny_mce_wrapper.definition': 'Definition',
23
+ 'windward.core.components.utils.tiny_mce_wrapper.alternate_forms': 'Alternate Forms',
24
+ 'windward.core.components.utils.tiny_mce_wrapper.related_terms': 'Related Terms'
25
+ }
26
+ }
27
+ })
28
+
29
+ // Create data-app element for Vuetify
30
+ const app = document.createElement('div')
31
+ app.setAttribute('data-app', 'true')
32
+ document.body.appendChild(app)
33
+ })
34
+
35
+ afterEach(() => {
36
+ wrapper.destroy()
37
+ // Clean up data-app element
38
+ const app = document.querySelector('[data-app=true]')
39
+ if (app) {
40
+ document.body.removeChild(app)
41
+ }
42
+ })
43
+
44
+ const mountComponent = (options = {}) => {
45
+ return mount(GlossaryToolTip, {
46
+ localVue,
47
+ vuetify,
48
+ i18n,
49
+ attachTo: document.body,
50
+ slots: {
51
+ term: '<span>Test Term</span>',
52
+ definition: '<span>Test Definition</span>'
53
+ },
54
+ ...options
55
+ })
56
+ }
57
+
58
+ const waitForMenuTransition = async () => {
59
+ await new Promise(resolve => setTimeout(resolve, 300))
60
+ await Vue.nextTick()
61
+ }
62
+
63
+ it('renders the term in the activator slot', () => {
64
+ wrapper = mountComponent()
65
+ expect(wrapper.find('.glossary-word').text()).toContain('Test Term')
66
+ })
67
+
68
+ it('starts with menu closed', () => {
69
+ wrapper = mountComponent()
70
+ expect(wrapper.vm.show).toBe(false)
71
+ expect(wrapper.find('.v-menu__content').exists()).toBe(false)
72
+ })
73
+
74
+ it('opens menu on click', async () => {
75
+ wrapper = mountComponent()
76
+ const activator = wrapper.find('.glossary-word span')
77
+
78
+ await activator.trigger('click')
79
+ await waitForMenuTransition()
80
+
81
+ expect(wrapper.vm.show).toBe(true)
82
+ const menu = document.querySelector('.v-menu__content')
83
+ expect(menu).toBeTruthy()
84
+ expect(getComputedStyle(menu).display).not.toBe('none')
85
+ })
86
+
87
+ it('closes menu when clicking outside', async () => {
88
+ wrapper = mountComponent()
89
+ const activator = wrapper.find('.glossary-word span')
90
+
91
+ // Open menu
92
+ await activator.trigger('click')
93
+ await waitForMenuTransition()
94
+ expect(wrapper.vm.show).toBe(true)
95
+
96
+ // Click outside
97
+ document.body.click()
98
+ await waitForMenuTransition()
99
+
100
+ expect(wrapper.vm.show).toBe(false)
101
+ })
102
+
103
+ it('renders definition content when menu is open', async () => {
104
+ wrapper = mountComponent()
105
+ const activator = wrapper.find('.glossary-word span')
106
+
107
+ await activator.trigger('click')
108
+ await waitForMenuTransition()
109
+
110
+ expect(document.body.textContent).toContain('Test Definition')
111
+ })
112
+
113
+ it('applies active class when menu is open', async () => {
114
+ wrapper = mountComponent()
115
+ const activator = wrapper.find('.glossary-word span')
116
+
117
+ await activator.trigger('click')
118
+ await waitForMenuTransition()
119
+
120
+ expect(wrapper.find('.glossary-word').classes()).toContain('active')
121
+ })
122
+
123
+ it('removes active class when menu is closed', async () => {
124
+ wrapper = mountComponent()
125
+ const activator = wrapper.find('.glossary-word span')
126
+
127
+ // Open then close
128
+ await activator.trigger('click')
129
+ await waitForMenuTransition()
130
+
131
+ document.body.click()
132
+ await waitForMenuTransition()
133
+
134
+ expect(wrapper.find('.glossary-word').classes()).not.toContain('active')
135
+ })
136
+ })