@windward/games 0.0.2 → 0.0.4

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 (27) hide show
  1. package/components/content/blocks/crosswordPuzzle/Crossword.ts +475 -0
  2. package/components/content/blocks/crosswordPuzzle/CrosswordElements.ts +36 -0
  3. package/components/content/blocks/crosswordPuzzle/CrosswordPuzzle.vue +542 -0
  4. package/components/content/blocks/dragDrop/BucketGame.vue +4 -4
  5. package/components/content/blocks/dragDrop/SortingGame.vue +1 -1
  6. package/components/content/blocks/flashcards/FlashcardSlides.vue +2 -2
  7. package/components/content/blocks/matchingGame/MatchingGame.vue +2 -2
  8. package/components/content/blocks/multipleChoice/MultipleChoice.vue +1 -1
  9. package/components/content/blocks/multipleChoice/QuestionDialog.vue +2 -2
  10. package/components/content/blocks/quizshowGame/QuizShow.vue +2 -2
  11. package/components/content/blocks/slideshow/SlideShow.vue +2 -2
  12. package/components/content/blocks/wordJumble/WordJumble.vue +24 -12
  13. package/components/settings/CrosswordPuzzleSettingsManager.vue +274 -0
  14. package/components/settings/WordJumbleSettingsManager.vue +4 -1
  15. package/i18n/en-US/components/content/blocks/crossword.ts +7 -0
  16. package/i18n/en-US/components/content/blocks/index.ts +2 -0
  17. package/i18n/en-US/components/settings/crossword.ts +6 -0
  18. package/i18n/en-US/components/settings/index.ts +2 -0
  19. package/i18n/en-US/components/settings/word_jumble.ts +1 -1
  20. package/i18n/en-US/shared/content_blocks.ts +1 -0
  21. package/i18n/en-US/shared/settings.ts +1 -0
  22. package/package.json +1 -1
  23. package/plugin.js +22 -1
  24. package/test/blocks/crossword/CrosswordPuzzle.spec.js +49 -0
  25. package/test/settings/BucketGameManager.spec.js +1 -1
  26. package/test/settings/CrosswordPuzzleManager.spec.js +103 -0
  27. package/test/settings/WordJumbleManager.spec.js +2 -2
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <v-container>
3
3
  <v-col class="pa-0">
4
- <h2>
4
+ <h3>
5
5
  {{
6
6
  block.metadata.config.title
7
7
  ? block.metadata.config.title
@@ -9,7 +9,7 @@
9
9
  'plugin.games.components.content.blocks.word_jumble.title'
10
10
  )
11
11
  }}
12
- </h2>
12
+ </h3>
13
13
  <p>{{ block.metadata.config.instructions }}</p>
14
14
  </v-col>
15
15
  <v-col class="pa-0">
@@ -19,7 +19,7 @@
19
19
  v-for="(word, index) in block.metadata.config.words"
20
20
  :key="index"
21
21
  >
22
- <v-row class="d-flex justify-center outline ma-2">
22
+ <v-row class="d-flex justify-center ma-2">
23
23
  <p class="pa-3 mb-0 clueAndJumble">
24
24
  <span class="spanBold"
25
25
  >{{
@@ -36,15 +36,22 @@
36
36
  {{ shuffle(word.value) }}
37
37
  </p>
38
38
  </v-row>
39
- <v-container :key="'feedback'" :class="feedbackStatus">
40
- <br />
41
- <v-row
42
- class="d-flex justify-space-around"
43
- align="center"
44
- justify="center"
45
- >{{ feedback }}
39
+ <v-container
40
+ v-if="showFeedback"
41
+ :key="'feedback'"
42
+ :class="feedbackStatus"
43
+ >
44
+ <v-row class="d-flex" align="center">
45
+ <v-col cols="4"></v-col>
46
+ <v-col cols="4" class="d-flex justify-center">
47
+ <p class="">{{ feedback }}</p>
48
+ </v-col>
49
+ <v-col cols="4" class="d-flex justify-end">
50
+ <v-icon @click="onHideFeedback">
51
+ mdi-alpha-x-circle-outline</v-icon
52
+ >
53
+ </v-col>
46
54
  </v-row>
47
- <br />
48
55
  </v-container>
49
56
  <v-row class="justify-center mt-4">
50
57
  <Jumble
@@ -124,6 +131,7 @@ export default {
124
131
  'plugin.games.components.content.blocks.word_jumble.feedback'
125
132
  ),
126
133
  showAnswer: false,
134
+ showFeedback: false,
127
135
  resetValue: false,
128
136
  studentResponse: '',
129
137
  feedbackStatus: '',
@@ -166,8 +174,12 @@ export default {
166
174
  // reveal prop changed to true to show answer
167
175
  this.showAnswer = true
168
176
  },
177
+ onHideFeedback() {
178
+ this.showFeedback = false
179
+ },
169
180
  onCheckAnswer(word) {
170
181
  this.studentResponse = this.studentResponse.toLowerCase()
182
+ this.showFeedback = true
171
183
  const answer = word.value.toLowerCase()
172
184
  if (this.studentResponse === answer) {
173
185
  // updates class
@@ -202,7 +214,7 @@ export default {
202
214
  },
203
215
  onSlideChanged() {
204
216
  // this function is called when the slide is changed
205
- // reset the game each time this occurs via props due to fact
217
+ // reset the game each time this occurs via props due to fact
206
218
  // that components do not remount on slides each time the slide is revisited
207
219
  // updates class
208
220
  this.feedbackStatus = ''
@@ -0,0 +1,274 @@
1
+ <template>
2
+ <div>
3
+ <v-form ref="form" v-model="valid" v-if="!loading">
4
+ <v-text-field
5
+ v-model="block.metadata.config.title"
6
+ :counter="50"
7
+ :label="
8
+ $t(
9
+ 'plugin.games.components.settings.bucket_game.form.title'
10
+ )
11
+ "
12
+ ref="title"
13
+ ></v-text-field>
14
+ <br />
15
+ <v-textarea
16
+ outlined
17
+ auto-grow
18
+ v-model="block.metadata.config.instructions"
19
+ :counter="255"
20
+ :label="
21
+ $t(
22
+ 'plugin.games.components.settings.bucket_game.form.instructions'
23
+ )
24
+ "
25
+ ></v-textarea>
26
+ <v-expansion-panels flat>
27
+ <v-expansion-panel>
28
+ <v-expansion-panel-header class="pa-0"
29
+ ><h3>
30
+ {{
31
+ $t(
32
+ 'plugin.games.components.settings.crossword.crossword_items'
33
+ )
34
+ }}
35
+ </h3></v-expansion-panel-header
36
+ >
37
+ <v-expansion-panel-content class="ma-0">
38
+ <v-container class="pa-0">
39
+ <p>
40
+ {{
41
+ $t(
42
+ 'plugin.games.components.settings.crossword.min_length'
43
+ )
44
+ }}
45
+ </p>
46
+ <v-card
47
+ v-for="(item, index) in block.metadata.config
48
+ .words"
49
+ :key="'bucket_' + index"
50
+ class="pa-2 flex-fill cardOutline"
51
+ elevation="0"
52
+ outlined
53
+ tile
54
+ >
55
+ <v-row class="itemHeight">
56
+ <p
57
+ class="text-truncate pa-0 ma-0 seventy"
58
+ @click="onOpenEditor(index)"
59
+ v-on:keyup.enter="onOpenEditor(index)"
60
+ @mouseover="onHover"
61
+ @mouseleave="onHoverLeave"
62
+ :class="cursor"
63
+ tabindex="0"
64
+ >
65
+ <v-icon class="pl-2"
66
+ >mdi-gesture-tap-hold</v-icon
67
+ >
68
+ {{
69
+ item.word
70
+ ? item.word
71
+ : $t(
72
+ 'plugin.games.components.settings.bucket_game.form.enter_text'
73
+ )
74
+ }}
75
+ </p>
76
+ <v-icon
77
+ class="twenty"
78
+ @click="onDelete(index)"
79
+ >mdi-delete-outline</v-icon
80
+ >
81
+ </v-row>
82
+ <v-textarea
83
+ v-if="item.expand"
84
+ outlined
85
+ :autofocus="true"
86
+ :label="
87
+ $t(
88
+ 'plugin.games.components.settings.crossword.word'
89
+ )
90
+ "
91
+ class="pt-4"
92
+ v-model="
93
+ block.metadata.config.words[index].word
94
+ "
95
+ ></v-textarea>
96
+ <v-textarea
97
+ v-if="item.expand"
98
+ outlined
99
+ :label="
100
+ $t(
101
+ 'plugin.games.components.settings.crossword.description'
102
+ )
103
+ "
104
+ class="pt-4"
105
+ v-model="
106
+ block.metadata.config.words[index]
107
+ .description
108
+ "
109
+ ></v-textarea>
110
+ </v-card>
111
+ <p
112
+ @mouseover="onHover"
113
+ @mouseleave="onHoverLeave"
114
+ @click="onAddElement"
115
+ v-on:keyup.enter="onAddElement"
116
+ class="fullWidth"
117
+ :class="cursor"
118
+ tabindex="0"
119
+ >
120
+ <v-icon class="primary addIcon"
121
+ >mdi-plus</v-icon
122
+ >
123
+ {{
124
+ $t(
125
+ 'plugin.games.components.content.blocks.crossword.add_element'
126
+ )
127
+ }}
128
+ </p>
129
+ </v-container>
130
+ </v-expansion-panel-content>
131
+ </v-expansion-panel>
132
+ </v-expansion-panels>
133
+ </v-form>
134
+ <div v-if="loading" class="text-center">
135
+ <v-progress-circular
136
+ :size="70"
137
+ :width="7"
138
+ color="primary"
139
+ indeterminate
140
+ ></v-progress-circular>
141
+ </div>
142
+ </div>
143
+ </template>
144
+
145
+ <script>
146
+ import _ from 'lodash'
147
+ import BaseContentSettings from '~/components/Content/Tool/BaseContentSettings.js'
148
+ export default {
149
+ name: 'CrosswordPuzzleSettingsManager',
150
+ extends: BaseContentSettings,
151
+ components: {},
152
+ beforeMount() {
153
+ if (_.isEmpty(this.block)) {
154
+ this.block = {}
155
+ }
156
+ if (_.isEmpty(this.block.metadata)) {
157
+ this.block.metadata = {}
158
+ }
159
+ if (_.isEmpty(this.block.metadata.config)) {
160
+ this.block.metadata.config = {}
161
+ }
162
+ if (_.isEmpty(this.block.metadata.config.title)) {
163
+ this.block.metadata.config.title = ''
164
+ }
165
+ if (_.isEmpty(this.block.metadata.config.instructions)) {
166
+ this.block.metadata.config.instructions = ''
167
+ }
168
+ if (_.isEmpty(this.block.metadata.config.words)) {
169
+ this.block.metadata.config.words = []
170
+ let counter = 0
171
+ while (counter < 2) {
172
+ const default_object = {
173
+ id: counter,
174
+ word: '',
175
+ description: '',
176
+ expand: false,
177
+ }
178
+ counter = counter + 1
179
+ !this.block.metadata.config.words.push(default_object)
180
+ }
181
+ }
182
+ },
183
+ data() {
184
+ return {
185
+ valid: true,
186
+ loading: false,
187
+ cursor: null,
188
+ }
189
+ },
190
+ methods: {
191
+ onAddElement() {
192
+ this.block.metadata.config.words.forEach((element) => {
193
+ element.expand = false
194
+ })
195
+ const default_object = {
196
+ id: this.block.metadata.config.words.length,
197
+ word: '',
198
+ description: '',
199
+ expand: true,
200
+ }
201
+ this.block.metadata.config.words.push(default_object)
202
+ },
203
+ onDelete(index) {
204
+ this.block.metadata.config.words.splice(index, 1)
205
+ let counter = 0
206
+ this.block.metadata.config.words.forEach((element) => {
207
+ element.id = counter
208
+ counter = counter + 1
209
+ })
210
+ if (this.block.metadata.config.words.length < 2) {
211
+ this.onAddElement()
212
+ }
213
+ },
214
+ onOpenEditor(index) {
215
+ this.block.metadata.config.words[index].expand =
216
+ !this.block.metadata.config.words[index].expand
217
+ this.block.metadata.config.words.forEach((element) => {
218
+ if (index !== element.id) {
219
+ element.expand = false
220
+ }
221
+ })
222
+ },
223
+ onHover(index) {
224
+ this.cursor = 'changePointer'
225
+ },
226
+ onHoverLeave() {
227
+ this.cursor = ''
228
+ },
229
+ },
230
+ }
231
+ </script>
232
+ <style lang="scss" scoped>
233
+ .v-progress-circular {
234
+ margin: 1rem;
235
+ }
236
+ .changePointer {
237
+ cursor: pointer !important;
238
+ }
239
+ .cardOutline {
240
+ border: 2px solid var(--v-primary-base);
241
+ margin-bottom: 8px;
242
+ }
243
+ .fullWidth {
244
+ display: flex;
245
+ justify-content: center;
246
+ align-items: center;
247
+ font-weight: bold;
248
+ font-size: 16px;
249
+ }
250
+ .itemHeight {
251
+ display: flex;
252
+ align-content: center;
253
+ justify-content: space-between;
254
+ height: 60px;
255
+ }
256
+ .seventy {
257
+ width: 70%;
258
+ height: 100%;
259
+ display: flex;
260
+ align-items: center;
261
+ }
262
+ .twenty {
263
+ width: 20%;
264
+ height: 90%;
265
+ margin-top: 3px;
266
+ border-left: 2px solid var(--v-primary-base);
267
+ }
268
+ .addIcon {
269
+ margin-right: 5px;
270
+ }
271
+ .v-expansion-panel-content::v-deep .v-expansion-panel-content__wrap {
272
+ padding: 0 !important;
273
+ }
274
+ </style>
@@ -209,11 +209,14 @@ export default {
209
209
  },
210
210
  methods: {
211
211
  onAddWord() {
212
+ this.block.metadata.config.words.forEach((element) => {
213
+ element.expand = false
214
+ })
212
215
  let defaultWord = {
213
216
  id: this.block.metadata.config.words.length + 1,
214
217
  value: '',
215
218
  hint: '',
216
- expand: false,
219
+ expand: true,
217
220
  }
218
221
  this.block.metadata.config.words.push(defaultWord)
219
222
  },
@@ -0,0 +1,7 @@
1
+ export default {
2
+ crossword: 'Crossword Game',
3
+ add_element: 'Add Element',
4
+ placeholder:
5
+ 'THIS IS EXAMPLE DATA! MUST ENTER MORE THAN TWO WORDS & DESCRIPTIONS TO SEE EDITED TEXT',
6
+ play_again: 'Play Again',
7
+ }
@@ -6,6 +6,7 @@ import quizshow_game from './quizshow_game'
6
6
  import slideshow from './slideshow'
7
7
  import multiple_choice from './multiple_choice'
8
8
  import word_jumble from './word_jumble'
9
+ import crossword from './crossword'
9
10
 
10
11
  export default {
11
12
  bucket_game,
@@ -16,4 +17,5 @@ export default {
16
17
  slideshow,
17
18
  multiple_choice,
18
19
  word_jumble,
20
+ crossword,
19
21
  }
@@ -0,0 +1,6 @@
1
+ export default {
2
+ crossword_items: 'Crossword Items',
3
+ word: 'Word',
4
+ description: 'Description',
5
+ min_length: 'Cannot have less than two words',
6
+ }
@@ -6,6 +6,7 @@ import slideshow from './slideshow'
6
6
  import multiple_choice from './multiple_choice'
7
7
  import sorting_game from './sorting_game'
8
8
  import word_jumble from './word_jumble'
9
+ import crossword from './crossword'
9
10
 
10
11
  export default {
11
12
  bucket_game,
@@ -16,4 +17,5 @@ export default {
16
17
  slideshow,
17
18
  multiple_choice,
18
19
  sorting_game,
20
+ crossword,
19
21
  }
@@ -4,7 +4,7 @@ export default {
4
4
  instructions: 'Instructions',
5
5
  click: 'Click to enter text',
6
6
  label: 'Word for jumbling',
7
- hint: 'Hint for word',
7
+ hint: 'Clue for word',
8
8
  add: 'Add Word',
9
9
  feedback_correct: 'Feedback when correct',
10
10
  feedback_incorrect: 'Feedback when incorrect',
@@ -8,6 +8,7 @@ export default {
8
8
  slideshow: 'Slideshow',
9
9
  multiple_choice: 'Multiple Choice',
10
10
  word_jumble: 'Word Jumble',
11
+ crossword_puzzle: 'Crossword',
11
12
  },
12
13
  grouping: {
13
14
  game: 'Activities',
@@ -8,5 +8,6 @@ export default {
8
8
  quizshow_manager: 'Quizshow Manager',
9
9
  multiple_choice_manager: 'Multiple Choice Manager',
10
10
  wordjumble_manager: 'Word Jumble Manager',
11
+ crossword_puzzle_manager: 'Crossword Puzzle Manager',
11
12
  },
12
13
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windward/games",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Windward UI Plugin Games",
5
5
  "main": "plugin.js",
6
6
  "scripts": {
package/plugin.js CHANGED
@@ -15,6 +15,8 @@ import quizShow from './components/content/blocks/quizshowGame/QuizShow'
15
15
  import QuizShowSettingsManager from './components/settings/QuizShowSettingsManager'
16
16
  import MultipleChoice from './components/content/blocks/multipleChoice/MultipleChoice.vue'
17
17
  import MultipleChoiceSettingsManager from './components/settings/MultipleChoiceSettingsManager'
18
+ import CrosswordPuzzle from './components/content/blocks/crosswordPuzzle/CrosswordPuzzle.vue'
19
+ import CrosswordPuzzleSettingsManager from './components/settings/CrosswordPuzzleSettingsManager.vue'
18
20
 
19
21
  export default {
20
22
  name: 'Windward Games Plugin',
@@ -107,12 +109,22 @@ export default {
107
109
  tag: 'games-wordjumble-game',
108
110
  template: WordJumble,
109
111
  metadata: {
110
- icon: 'mdi-alpha-w-box-outline',
112
+ icon: 'mdi-ab-testing',
111
113
  name: 'plugin.games.shared.content_blocks.title.word_jumble',
112
114
  grouping:
113
115
  'plugin.games.shared.content_blocks.grouping.game',
114
116
  },
115
117
  },
118
+ {
119
+ tag: 'games-crossword-puzzle-game',
120
+ template: CrosswordPuzzle,
121
+ metadata: {
122
+ icon: 'mdi-alpha-c-box-outline',
123
+ name: 'plugin.games.shared.content_blocks.title.crossword_puzzle',
124
+ grouping:
125
+ 'plugin.games.shared.content_blocks.grouping.game',
126
+ },
127
+ },
116
128
  ],
117
129
  settings: [
118
130
  {
@@ -187,6 +199,15 @@ export default {
187
199
  name: 'plugin.games.shared.settings.title.wordjumble_manager',
188
200
  },
189
201
  },
202
+ {
203
+ tag: 'games-crossword-puzzle-settings',
204
+ template: CrosswordPuzzleSettingsManager,
205
+ context: ['block.games-crossword-puzzle-game'],
206
+ metadata: {
207
+ icon: 'mdi-cog',
208
+ name: 'plugin.games.shared.settings.title.crossword_puzzle_manager',
209
+ },
210
+ },
190
211
  ],
191
212
  },
192
213
  }
@@ -0,0 +1,49 @@
1
+ import { shallowMount } from '@vue/test-utils'
2
+ import { defaultMocks } from '@windward/core/test/mocks'
3
+ import CrosswordPuzzle from '@/components/content/blocks/crosswordPuzzle/CrosswordPuzzle'
4
+
5
+ import Vuetify from 'vuetify'
6
+ import Vue from 'vue'
7
+ Vue.use(Vuetify)
8
+
9
+ //override default mock
10
+ jest.mock(
11
+ 'lodash',
12
+ () => {
13
+ return {
14
+ cloneDeep: () => {
15
+ return []
16
+ },
17
+ isEmpty: () => {
18
+ return jest.fn()
19
+ },
20
+ flatten: () => {
21
+ return jest.fn()
22
+ },
23
+ get: () => {
24
+ return jest.fn()
25
+ },
26
+ }
27
+ },
28
+ { virtual: true }
29
+ )
30
+ describe('CrosswordPuzzle', () => {
31
+ test('is a Vue instance', () => {
32
+ const wrapper = shallowMount(CrosswordPuzzle, {
33
+ propsData: {
34
+ value: {
35
+ id: '00000000-0000-0000-0000-000000000000',
36
+ name: 'Test Content Page',
37
+ body: '[]',
38
+ metadata: {
39
+ config: {
40
+ words: [],
41
+ },
42
+ },
43
+ },
44
+ },
45
+ mocks: defaultMocks,
46
+ })
47
+ expect(wrapper.vm).toBeTruthy()
48
+ })
49
+ })
@@ -9,7 +9,7 @@ Vue.use(Vuetify)
9
9
  const settings = {}
10
10
  const context = 'block'
11
11
 
12
- describe('Matching game manager', () => {
12
+ describe('Bucket game manager', () => {
13
13
  test('is a Vue instance', () => {
14
14
  const wrapper = shallowMount(BucketGameSettingsManager, {
15
15
  propsData: {
@@ -0,0 +1,103 @@
1
+ import { shallowMount } from '@vue/test-utils'
2
+ import { defaultMocks } from '@windward/core/test/mocks'
3
+ import CrosswordPuzzleSettingsManager from '@/components/settings/CrosswordPuzzleSettingsManager'
4
+
5
+ import Vuetify from 'vuetify'
6
+ import Vue from 'vue'
7
+ Vue.use(Vuetify)
8
+
9
+ const settings = {}
10
+ const context = 'block'
11
+
12
+ describe('Crossword game manager', () => {
13
+ test('is a Vue instance', () => {
14
+ const wrapper = shallowMount(CrosswordPuzzleSettingsManager, {
15
+ propsData: {
16
+ block: {
17
+ id: '00000000-0000-0000-0000-000000000000',
18
+ name: 'Test Content Page',
19
+ body: '{}',
20
+ metadata: { config: {} },
21
+ },
22
+ settings: settings,
23
+ context: context,
24
+ data: {},
25
+ metadata: { config: {} },
26
+ },
27
+ mocks: defaultMocks,
28
+ })
29
+ expect(wrapper.vm).toBeTruthy()
30
+ })
31
+
32
+ test('can add new word', () => {
33
+ const wrapper = shallowMount(CrosswordPuzzleSettingsManager, {
34
+ propsData: {
35
+ block: {
36
+ id: '00000000-0000-0000-0000-000000000000',
37
+ name: 'Test Content Page',
38
+ body: '{}',
39
+ metadata: { config: {} },
40
+ },
41
+ settings: settings,
42
+ context: context,
43
+ data: {},
44
+ metadata: { config: {} },
45
+ },
46
+ mocks: defaultMocks,
47
+ })
48
+ wrapper.vm.onAddElement()
49
+ expect(wrapper.vm.block.metadata.config.words).toEqual([
50
+ {
51
+ id: 0,
52
+ word: '',
53
+ description: '',
54
+ expand: false,
55
+ },
56
+ {
57
+ id: 1,
58
+ word: '',
59
+ description: '',
60
+ expand: false,
61
+ },
62
+ {
63
+ id: 2,
64
+ word: '',
65
+ description: '',
66
+ expand: true,
67
+ },
68
+ ])
69
+ })
70
+
71
+ test('can delete word', () => {
72
+ const wrapper = shallowMount(CrosswordPuzzleSettingsManager, {
73
+ propsData: {
74
+ block: {
75
+ id: '00000000-0000-0000-0000-000000000000',
76
+ name: 'Test Content Page',
77
+ body: '{}',
78
+ metadata: { config: {} },
79
+ },
80
+ settings: settings,
81
+ context: context,
82
+ data: {},
83
+ metadata: { config: {} },
84
+ },
85
+ mocks: defaultMocks,
86
+ })
87
+ wrapper.vm.onDelete(1)
88
+ expect(wrapper.vm.block.metadata.config.words).toEqual([
89
+ {
90
+ id: 0,
91
+ word: '',
92
+ description: '',
93
+ expand: false,
94
+ },
95
+ {
96
+ id: 1,
97
+ word: '',
98
+ description: '',
99
+ expand: true,
100
+ },
101
+ ])
102
+ })
103
+ })