@saooti/octopus-sdk 41.5.5 → 41.5.6

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,12 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 41.5.6 (20/02/2026)
4
+
5
+ **Misc**
6
+
7
+ - Ajout d'un props pour ajouter une entrée permettant de sélectionner toutes les
8
+ entrées dans `ClassicMultiselect`
9
+
3
10
  ## 41.5.5 (16/02/2026)
4
11
 
5
12
  **Fix**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saooti/octopus-sdk",
3
- "version": "41.5.5",
3
+ "version": "41.5.6",
4
4
  "private": false,
5
5
  "description": "Javascript SDK for using octopus",
6
6
  "author": "Saooti",
@@ -48,7 +48,7 @@
48
48
  "stylelint-config-standard-scss": "^15.0.1",
49
49
  "stylelint-config-standard-vue": "^1.0.0",
50
50
  "stylelint-gamut": "^1.3.4",
51
- "swiper": "^11.2.10",
51
+ "swiper": "^12.1.2",
52
52
  "typescript-eslint": "^8.47.0",
53
53
  "video.js": "^8.23.6",
54
54
  "videojs-quality-selector-hls": "^1.1.1",
@@ -56,21 +56,33 @@
56
56
  @option:selected="onOptionSelected"
57
57
  @option:deselected="onOptionDeselect"
58
58
  >
59
+
59
60
  <template v-if="optionCustomTemplating.length" #option="option">
60
61
  <slot :name="optionCustomTemplating" :option="option" />
61
62
  </template>
63
+ <template v-else-if="withSelectAll" #option="option">
64
+ <strong v-if="option.id === selectAll.id">
65
+ {{ option[optionLabel] }}
66
+ </strong>
67
+ <span v-else>
68
+ {{ option[optionLabel] }}
69
+ </span>
70
+ </template>
71
+
62
72
  <template
63
73
  v-if="optionSelectedCustomTemplating.length"
64
74
  #selected-option="option"
65
75
  >
66
76
  <slot :name="optionSelectedCustomTemplating" :option="option" />
67
77
  </template>
78
+
68
79
  <template #no-options="{ searching }">
69
80
  <span v-if="searching">{{
70
81
  t("No elements found. Consider changing the search query.")
71
82
  }}</span>
72
83
  <span v-else>{{ t("List is empty") }}</span>
73
84
  </template>
85
+
74
86
  <template #list-footer>
75
87
  <div v-if="remainingElements" class="vs__dropdown-option">
76
88
  {{
@@ -81,11 +93,13 @@
81
93
  }}
82
94
  </div>
83
95
  </template>
96
+
84
97
  <template #list-header>
85
98
  <div v-if="maxOptionsSelected" class="vs__dropdown-option">
86
99
  {{ t("Multiselect max options", { max: maxOptions }) }}
87
100
  </div>
88
101
  </template>
102
+
89
103
  <template #open-indicator="{ attributes }">
90
104
  <ChevronDownIcon v-bind="attributes" />
91
105
  </template>
@@ -112,12 +126,14 @@ const {
112
126
  inModal = false, multiple = false, isDisabled = false, width = "100%",
113
127
  maxElement = 50, minSearchLength = 3, noDeselect = true, displayLabel = false,
114
128
  allowEmpty = true, optionChosen, maxOptions = null,
115
- optionCustomTemplating = '', optionSelectedCustomTemplating = ''
129
+ optionCustomTemplating = '', optionSelectedCustomTemplating = '',
130
+ optionLabel,
131
+ withSelectAll = false
116
132
  } = defineProps<{
117
133
  id?: string;
118
134
  label?: string;
119
135
  placeholder?: string;
120
- optionLabel?: string;
136
+ optionLabel?: keyof T;
121
137
  inModal?: boolean;
122
138
  multiple?: boolean;
123
139
  isDisabled?: boolean;
@@ -137,10 +153,24 @@ const {
137
153
  displayRequired?: boolean;
138
154
  popover?: string;
139
155
  popoverRelativeClass?: string;
156
+ /**
157
+ * Add an option to select everything at once
158
+ * If set to a truthy string, the added option will have this value as its
159
+ * label. If set to true, use a generic label.
160
+ */
161
+ withSelectAll?: boolean|string;
140
162
  }>();
141
163
 
142
164
  //Emits
143
- const emit = defineEmits(["onSearch", "selected", "onClose"]);
165
+ const emit = defineEmits<{
166
+ (e: "onSearch", query: string): void;
167
+ (e: "selected", data: Array<T>|T): void;
168
+ (e: "onClose", data: string): void;
169
+ }>();
170
+
171
+ //Composables
172
+ const { t } = useI18n();
173
+
144
174
 
145
175
  //Data
146
176
  const optionSelected : Ref<T|T[]>= ref(undefined);
@@ -148,10 +178,10 @@ const options : Ref<Array<T>>= ref([]);
148
178
  const remainingElements = ref(0);
149
179
  const isLoading = ref(false);
150
180
  const searchInput = ref("");
151
-
152
- //Composables
153
- const { t } = useI18n();
154
-
181
+ const selectAll = {
182
+ id: 'SELECT_ALL',
183
+ [optionLabel]: withSelectAll && typeof withSelectAll === 'string' ? withSelectAll : t('All')
184
+ } as unknown as T;
155
185
 
156
186
  //Computed
157
187
  const maxOptionsSelected = computed(() => {
@@ -178,6 +208,7 @@ watch(optionSelected, () => {
178
208
  function fakeSearch(): Array<unknown> {
179
209
  return options.value;
180
210
  }
211
+
181
212
  function onSearch(search?: string): void {
182
213
  if (search && search.length < minSearchLength) {
183
214
  return;
@@ -187,30 +218,44 @@ function onSearch(search?: string): void {
187
218
  isLoading.value = true;
188
219
  emit("onSearch", search);
189
220
  }
221
+
190
222
  function onClose() {
191
223
  emit("onClose", searchInput.value);
192
224
  searchInput.value = "";
193
225
  }
226
+
194
227
  function afterSearch(optionsFetched: Array<T>, count: number): void {
195
- options.value = optionsFetched;
228
+ if (withSelectAll) {
229
+ options.value = [selectAll, ...optionsFetched];
230
+ count += 1;
231
+ } else {
232
+ options.value = optionsFetched;
233
+ }
196
234
  remainingElements.value = Math.max(0, count - maxElement);
197
235
  isLoading.value = false;
198
236
  }
199
- function onOptionSelected(optionSelected: unknown): void {
200
- emit("selected", optionSelected);
237
+
238
+ function onOptionSelected(newValue: Array<T>|T): void {
239
+ // Check if selectAll is included
240
+ if (withSelectAll && Array.isArray(newValue) && newValue.find(o => o.id === selectAll.id)) {
241
+ emit("selected", options.value.slice(1, -1));
242
+ } else {
243
+ emit("selected", newValue);
244
+ }
201
245
  }
202
- function onOptionDeselect(event: unknown): void {
246
+
247
+ function onOptionDeselect(event: T): void {
203
248
  if (!multiple) {
204
249
  return;
205
250
  }
206
251
  if (
207
252
  !allowEmpty &&
208
- 0 === (optionSelected.value as Array<unknown>).length
253
+ 0 === (optionSelected.value as Array<T>).length
209
254
  ) {
210
- (optionSelected.value as Array<unknown>).push(event);
255
+ (optionSelected.value as Array<T>).push(event);
211
256
  return;
212
257
  }
213
- emit("selected", optionSelected.value);
258
+ emit("selected", (optionSelected.value as Array<T>));
214
259
  }
215
260
 
216
261
  //Expose
@@ -415,5 +415,6 @@
415
415
  "Miniplayer - Parameters - Auto height": "Automatische Höhe",
416
416
  "Edit": "Bearbeiten",
417
417
  "Podcast": "Folge",
418
- "Player - Transcription - AI Warning": "Die Transkription basiert auf KI und kann Fehler enthalten, bitte teilen Sie uns dies mit."
418
+ "Player - Transcription - AI Warning": "Die Transkription basiert auf KI und kann Fehler enthalten, bitte teilen Sie uns dies mit.",
419
+ "All organisations": "Alle Organisationen"
419
420
  }
@@ -415,5 +415,6 @@
415
415
  "Miniplayer - Parameters - Auto height": "Auto Height",
416
416
  "Edit": "Edit",
417
417
  "Podcast": "Episode",
418
- "Player - Transcription - AI Warning": "The transcription is based on AI and may contain errors, please let us know."
418
+ "Player - Transcription - AI Warning": "The transcription is based on AI and may contain errors, please let us know.",
419
+ "All organisations": "All organizations"
419
420
  }
@@ -415,5 +415,6 @@
415
415
  "Miniplayer - Parameters - Auto height": "Altura automática",
416
416
  "Edit": "Editar",
417
417
  "Podcast": "Episodio",
418
- "Player - Transcription - AI Warning": "La transcripción se basa en IA y puede contener errores, háganoslo saber."
418
+ "Player - Transcription - AI Warning": "La transcripción se basa en IA y puede contener errores, háganoslo saber.",
419
+ "All organisations": "Todas las organizaciones"
419
420
  }
@@ -24,6 +24,7 @@
24
24
  "Producted by : ": "Produit par : ",
25
25
  "Loading podcasts ...": "Chargement des épisodes…",
26
26
  "All podcasts": "Tous les épisodes",
27
+ "All organisations": "Toutes les organisations",
27
28
  "Error": "Erreur",
28
29
  "Upload": "Téléverser",
29
30
  "Count more elements matched your query, please make a more specific search.":
@@ -416,5 +416,6 @@
416
416
  "Miniplayer - Parameters - Auto height": "Altezza automatica",
417
417
  "Edit": "Modificare",
418
418
  "Podcast": "Episodio",
419
- "Player - Transcription - AI Warning": "La trascrizione si basa sull'intelligenza artificiale e potrebbe contenere errori, faccelo sapere."
419
+ "Player - Transcription - AI Warning": "La trascrizione si basa sull'intelligenza artificiale e potrebbe contenere errori, faccelo sapere.",
420
+ "All organisations": "Tutte le organizzazioni"
420
421
  }
@@ -414,5 +414,6 @@
414
414
  "Miniplayer - Parameters - Auto height": "Samodejna višina",
415
415
  "Edit": "Uredi",
416
416
  "Podcast": "Epizoda",
417
- "Player - Transcription - AI Warning": "Transkripcija temelji na umetni inteligenci in lahko vsebuje napake, zato nas obvestite."
417
+ "Player - Transcription - AI Warning": "Transkripcija temelji na umetni inteligenci in lahko vsebuje napake, zato nas obvestite.",
418
+ "All organisations": "Vse organizacije"
418
419
  }
@@ -0,0 +1,186 @@
1
+ import '@tests/mocks/useRouter';
2
+ import '@tests/mocks/useAdvancedParamInit';
3
+ import '@tests/mocks/i18n';
4
+
5
+ import AdvancedSearch from '@/components/display/filter/AdvancedSearch.vue';
6
+ import { mount } from '@tests/utils';
7
+ import { describe, expect, it, vi, beforeEach } from 'vitest';
8
+ import { useAuthStore } from '@/stores/AuthStore';
9
+ import { state } from '@/stores/ParamSdkStore';
10
+ import { useGeneralStore } from '@/stores/GeneralStore';
11
+
12
+ // Mock the useOrgaComputed composable
13
+ vi.mock('@/components/composable/useOrgaComputed', () => ({
14
+ useOrgaComputed: () => {
15
+ const { computed } = require('vue');
16
+ return {
17
+ isPodcastmaker: computed(() => state.generalParameters.podcastmaker as boolean),
18
+ isEditRights: (orgaId?: string) => {
19
+ const authStore = useAuthStore();
20
+ return authStore.authOrgaId === orgaId || authStore.isRoleAdmin;
21
+ }
22
+ };
23
+ }
24
+ }));
25
+
26
+ // Mock the groups API
27
+ vi.mock('@/api/groupsApi', () => {
28
+ return {
29
+ groupsApi: {
30
+ count: vi.fn().mockResolvedValue(0)
31
+ }
32
+ };
33
+ });
34
+
35
+ describe('AdvancedSearch - isSelectValidity computed (fix for isPodcastmaker.value)', () => {
36
+ beforeEach(() => {
37
+ // Reset the podcastmaker state before each test
38
+ state.generalParameters.podcastmaker = false;
39
+ });
40
+
41
+ it('should correctly evaluate isSelectValidity when isPodcastmaker is false', async () => {
42
+ const wrapper = await mount(AdvancedSearch, {
43
+ props: {
44
+ organisationId: 'test-org-id',
45
+ isEmission: false,
46
+ includeHidden: true
47
+ },
48
+ beforeMount: async () => {
49
+ const authStore = useAuthStore();
50
+ authStore.$patch({
51
+ authOrgaId: 'test-org-id',
52
+ authRole: ['PODCAST_CRUD'],
53
+ authOrganisation: {
54
+ id: 'test-org-id',
55
+ name: 'Test Organisation',
56
+ imageUrl: '',
57
+ attributes: {}
58
+ }
59
+ });
60
+
61
+ const generalStore = useGeneralStore();
62
+ generalStore.$patch({
63
+ platformEducation: false
64
+ });
65
+
66
+ // Ensure isPodcastmaker is false
67
+ state.generalParameters.podcastmaker = false;
68
+ }
69
+ });
70
+
71
+ // Access the component's computed property
72
+ const vm = wrapper.vm as any;
73
+
74
+ // When isPodcastmaker is false and other conditions are met,
75
+ // isSelectValidity should be true
76
+ expect(vm.isSelectValidity).toBe(true);
77
+ });
78
+
79
+ it('should correctly evaluate isSelectValidity when isPodcastmaker is true', async () => {
80
+ const wrapper = await mount(AdvancedSearch, {
81
+ props: {
82
+ organisationId: 'test-org-id',
83
+ isEmission: false,
84
+ includeHidden: true
85
+ },
86
+ beforeMount: async () => {
87
+ const authStore = useAuthStore();
88
+ authStore.$patch({
89
+ authOrgaId: 'test-org-id',
90
+ authRole: ['PODCAST_CRUD'],
91
+ authOrganisation: {
92
+ id: 'test-org-id',
93
+ name: 'Test Organisation',
94
+ imageUrl: '',
95
+ attributes: {}
96
+ }
97
+ });
98
+
99
+ const generalStore = useGeneralStore();
100
+ generalStore.$patch({
101
+ platformEducation: false
102
+ });
103
+
104
+ // Set isPodcastmaker to true
105
+ state.generalParameters.podcastmaker = true;
106
+ }
107
+ });
108
+
109
+ // Access the component's computed property
110
+ const vm = wrapper.vm as any;
111
+
112
+ // When isPodcastmaker is true, isSelectValidity should be false
113
+ // (because the condition includes !isPodcastmaker.value)
114
+ expect(vm.isSelectValidity).toBe(false);
115
+ });
116
+
117
+ it('should correctly evaluate isSelectValidity when isEmission is true', async () => {
118
+ const wrapper = await mount(AdvancedSearch, {
119
+ props: {
120
+ organisationId: 'test-org-id',
121
+ isEmission: true, // This should make isSelectValidity false
122
+ includeHidden: true
123
+ },
124
+ beforeMount: async () => {
125
+ const authStore = useAuthStore();
126
+ authStore.$patch({
127
+ authOrgaId: 'test-org-id',
128
+ authRole: ['PODCAST_CRUD'],
129
+ authOrganisation: {
130
+ id: 'test-org-id',
131
+ name: 'Test Organisation',
132
+ imageUrl: '',
133
+ attributes: {}
134
+ }
135
+ });
136
+
137
+ const generalStore = useGeneralStore();
138
+ generalStore.$patch({
139
+ platformEducation: false
140
+ });
141
+
142
+ state.generalParameters.podcastmaker = false;
143
+ }
144
+ });
145
+
146
+ const vm = wrapper.vm as any;
147
+
148
+ // When isEmission is true, isSelectValidity should be false
149
+ expect(vm.isSelectValidity).toBe(false);
150
+ });
151
+
152
+ it('should correctly evaluate isSelectValidity when includeHidden is false', async () => {
153
+ const wrapper = await mount(AdvancedSearch, {
154
+ props: {
155
+ organisationId: 'test-org-id',
156
+ isEmission: false,
157
+ includeHidden: false // This should make isSelectValidity false
158
+ },
159
+ beforeMount: async () => {
160
+ const authStore = useAuthStore();
161
+ authStore.$patch({
162
+ authOrgaId: 'test-org-id',
163
+ authRole: ['PODCAST_CRUD'],
164
+ authOrganisation: {
165
+ id: 'test-org-id',
166
+ name: 'Test Organisation',
167
+ imageUrl: '',
168
+ attributes: {}
169
+ }
170
+ });
171
+
172
+ const generalStore = useGeneralStore();
173
+ generalStore.$patch({
174
+ platformEducation: false
175
+ });
176
+
177
+ state.generalParameters.podcastmaker = false;
178
+ }
179
+ });
180
+
181
+ const vm = wrapper.vm as any;
182
+
183
+ // When includeHidden is false, isSelectValidity should be false
184
+ expect(vm.isSelectValidity).toBe(false);
185
+ });
186
+ });