@ouestfrance/sipa-bms-ui 8.0.1 → 8.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouestfrance/sipa-bms-ui",
3
- "version": "8.0.1",
3
+ "version": "8.1.0",
4
4
  "author": "Ouest-France BMS",
5
5
  "license": "ISC",
6
6
  "scripts": {
@@ -16,6 +16,7 @@ describe('BmsInputText', () => {
16
16
 
17
17
  expect(inputElement.element.value).toStrictEqual('toto');
18
18
  });
19
+
19
20
  it('should react correctly on input', async () => {
20
21
  const { wrapper, inputElement } = factory({ modelValue: 'toto' });
21
22
 
@@ -34,4 +35,15 @@ describe('BmsInputText', () => {
34
35
  await nextTick();
35
36
  expect(wrapper.emitted()['update:modelValue']).toBeUndefined();
36
37
  });
38
+
39
+ it('should emit blur event for form validation', async () => {
40
+ const { wrapper, inputElement } = factory({
41
+ modelValue: 'toto',
42
+ disabled: true,
43
+ });
44
+
45
+ inputElement.setValue('titi');
46
+ await nextTick();
47
+ expect(wrapper.emitted()['update:modelValue']).toBeUndefined();
48
+ });
37
49
  });
@@ -17,11 +17,16 @@
17
17
  :disabled="disabled"
18
18
  :errors="errors"
19
19
  :hasDate="false"
20
+ @blur="$emits('blur')"
20
21
  @input="onInput"
21
22
  @keyup="onInput"
22
23
  >
23
- <template #icon-start><slot name="icon-start"></slot></template>
24
- <template #icon-end><slot name="icon-end"></slot></template>
24
+ <template #icon-start>
25
+ <slot name="icon-start"></slot>
26
+ </template>
27
+ <template #icon-end>
28
+ <slot name="icon-end"></slot>
29
+ </template>
25
30
  </RawInputText>
26
31
  </field>
27
32
  </template>
@@ -60,6 +65,7 @@ const input: Ref<HTMLElement | null> = ref(null);
60
65
 
61
66
  const $emits = defineEmits<{
62
67
  (e: 'update:modelValue', value: string): void;
68
+ (e: 'blur'): void;
63
69
  }>();
64
70
 
65
71
  const setFocus = () => {
@@ -14,6 +14,7 @@
14
14
  :placeholder="placeholder"
15
15
  :required="required"
16
16
  :disabled="disabled"
17
+ @blur="$emits('blur')"
17
18
  @input="onInput"
18
19
  @keydown.up="$emits('keyUp')"
19
20
  @keydown.down="$emits('keyDown')"
@@ -54,6 +55,7 @@ const input: Ref<HTMLElement | null> = ref(null);
54
55
  const $emits = defineEmits<{
55
56
  (e: 'update:modelValue', value: string): void;
56
57
  (e: 'update:focus', value: boolean): void;
58
+ (e: 'blur'): void;
57
59
  (e: 'keyUp'): void;
58
60
  (e: 'keyDown'): void;
59
61
  (e: 'keyEnter'): void;
@@ -0,0 +1,33 @@
1
+ import { BMS_FORM_VALID_URL_REGEX } from './form.helper';
2
+ import { describe, expect, test } from 'vitest';
3
+
4
+ describe('BMS_FORM_VALID_URL_REGEX', () => {
5
+ test.each([
6
+ // URLs valides
7
+ { url: 'https://google.com', expected: true },
8
+ { url: 'http://google.com', expected: true },
9
+ { url: 'google.com', expected: true },
10
+ { url: 'sub.domain.com', expected: true },
11
+ { url: 'domain.com/path', expected: true },
12
+ { url: 'domain.com/path/to/resource', expected: true },
13
+ { url: 'domain.com/path-with-dash', expected: true },
14
+ { url: 'domain.com/', expected: true },
15
+ { url: 'domaine-avec-tiret.fr', expected: true },
16
+ { url: 'ouest-france.fr/actualite', expected: true },
17
+ { url: 'http://UPPERCASE.com', expected: true }, // domaine en majuscules
18
+
19
+ // URLs invalides
20
+ { url: 'localhost:8080/actualite', expected: true },
21
+ { url: '', expected: false },
22
+ { url: 'not an url', expected: false },
23
+ { url: 'http:/domain.com', expected: false }, // slash manquant
24
+ { url: 'http://', expected: false }, // domaine manquant
25
+ { url: 'domain', expected: false }, // pas d'extension
26
+ { url: '.com', expected: false }, // pas de domaine
27
+ { url: 'domain.toolongtld', expected: false }, // tld trop long
28
+ { url: 'ftp://domain.com', expected: false }, // protocole non supporté
29
+ { url: 'javascript:alert(1)', expected: false }, // injection JavaScript
30
+ ])('should validate "$url" as $expected', ({ url, expected }) => {
31
+ expect(BMS_FORM_VALID_URL_REGEX.test(url)).toBe(expected);
32
+ });
33
+ });
@@ -0,0 +1,2 @@
1
+ export const BMS_FORM_VALID_URL_REGEX =
2
+ /^(https?:\/\/)?(localhost(:\d+)?|([\da-zA-Z][-\da-zA-Z]*[\da-zA-Z]\.)+[a-zA-Z]{2,6})(\/[\w .-]*)*\/?$/;
@@ -225,6 +225,18 @@
225
225
 
226
226
  <hr />
227
227
 
228
+ <BmsInputText
229
+ ref="inputText1"
230
+ label="input whith form validation"
231
+ required
232
+ v-model="text2"
233
+ :captions="captionsText2"
234
+ @blur="onBlurMyInputText"
235
+ placeholder="This is a placeholder in url format"
236
+ />
237
+
238
+ <hr />
239
+
228
240
  <BmsSelect
229
241
  label="select"
230
242
  required
@@ -307,6 +319,7 @@ import {
307
319
  BmsInputText,
308
320
  BmsSelect,
309
321
  BmsTextArea,
322
+ StatusType,
310
323
  } from '@/index';
311
324
  import BmsInputToggle from '@/components/form/BmsInputToggle.vue';
312
325
  import BmsInputRadio from '@/components/form/BmsInputRadio.vue';
@@ -319,9 +332,12 @@ import BmsInputCheckboxCaption from '@/components/form/BmsInputCheckboxCaption.v
319
332
  import BmsInputCheckboxCaptionGroup from '@/components/form/BmsInputCheckboxCaptionGroup.vue';
320
333
  import { Copy } from 'lucide-vue-next';
321
334
  import BmsIconButton from '@/components/button/BmsIconButton.vue';
335
+ import { BMS_FORM_VALID_URL_REGEX } from '@/helpers/form.helper';
322
336
 
323
337
  const { success } = useNotifications();
324
338
  const text = ref('');
339
+ const text2 = ref('');
340
+ const captionsText2 = ref();
325
341
  const selected = ref('value1');
326
342
  const selected2 = ref('');
327
343
  const files: Ref<File[]> = ref([]);
@@ -366,6 +382,16 @@ const onSubmit = (e: any) => {
366
382
  e.preventDefault();
367
383
  success(text.value + ' ' + selected.value);
368
384
  };
385
+
386
+ const onBlurMyInputText = () => {
387
+ if (!text2.value || BMS_FORM_VALID_URL_REGEX.test(text2.value)) {
388
+ captionsText2.value = [];
389
+ } else {
390
+ captionsText2.value = [
391
+ { label: 'Veuillez entrer une URL valide', mode: StatusType.Warning },
392
+ ];
393
+ }
394
+ };
369
395
  </script>
370
396
 
371
397
  <style lang="scss" scoped>
@@ -378,6 +404,7 @@ const onSubmit = (e: any) => {
378
404
  display: flex;
379
405
  gap: 8px;
380
406
  }
407
+
381
408
  hr {
382
409
  border: none;
383
410
  border-top: 3px double #333;