@ouestfrance/sipa-bms-ui 7.14.1 → 8.0.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/README.md +18 -12
- package/dist/components/form/BmsAutocomplete.vue.d.ts +8 -8
- package/dist/components/form/BmsInputText.vue.d.ts +13 -13
- package/dist/components/form/BmsSearch.vue.d.ts +13 -13
- package/dist/components/form/BmsSelect.vue.d.ts +9 -9
- package/dist/components/form/RawAutocomplete.vue.d.ts +30 -21
- package/dist/components/form/RawInputText.vue.d.ts +6 -6
- package/dist/components/navigation/UiTenantSwitcher.vue.d.ts +13 -13
- package/dist/components/table/BmsTableFilters.vue.d.ts +13 -13
- package/dist/mockServiceWorker.js +1 -1
- package/dist/models/form.model.d.ts +6 -0
- package/dist/plugins/field/FieldDatalist.spec.d.ts +1 -0
- package/dist/plugins/field/FieldDatalist.vue.d.ts +13 -12
- package/dist/sipa-bms-ui.css +81 -73
- package/dist/sipa-bms-ui.es.js +3405 -3315
- package/dist/sipa-bms-ui.es.js.map +1 -1
- package/dist/sipa-bms-ui.umd.js +3404 -3314
- package/dist/sipa-bms-ui.umd.js.map +1 -1
- package/package.json +13 -13
- package/src/components/form/BmsAutocomplete.vue +14 -8
- package/src/components/form/BmsSelect.spec.ts +5 -14
- package/src/components/form/BmsSelect.vue +8 -52
- package/src/components/form/RawAutocomplete.spec.ts +20 -17
- package/src/components/form/RawAutocomplete.vue +56 -53
- package/src/components/layout/BmsForm.stories.js +1 -1
- package/src/components/layout/BmsForm.vue +9 -5
- package/src/components/layout/BmsForm_retrocompat.stories.js +32 -0
- package/src/components/table/BmsTableFilters.vue +2 -2
- package/src/components/table/UiBmsTable.vue +4 -1
- package/src/models/form.model.ts +7 -0
- package/src/pages/Form.stories.js +37 -37
- package/src/plugins/field/FieldDatalist.spec.ts +35 -0
- package/src/plugins/field/FieldDatalist.stories.js +10 -0
- package/src/plugins/field/FieldDatalist.vue +85 -7
- package/src/showroom/pages/autocomplete.vue +7 -0
- package/src/showroom/pages/forms.vue +1 -1
|
@@ -36,43 +36,43 @@ const Template = (args) => ({
|
|
|
36
36
|
return { args };
|
|
37
37
|
},
|
|
38
38
|
template: `
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
<
|
|
49
|
-
|
|
50
|
-
<
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
39
|
+
<BmsContentPageLayout>
|
|
40
|
+
<template #header>
|
|
41
|
+
<BmsHeaderTitle title="Le Titre de la page" />
|
|
42
|
+
</template>
|
|
43
|
+
<template #breadcrumb>
|
|
44
|
+
<BmsBreadcrumb :breadcrumbs="args.breadcrumb" />
|
|
45
|
+
</template>
|
|
46
|
+
<template #default>
|
|
47
|
+
<BmsForm>
|
|
48
|
+
<template #form_1>
|
|
49
|
+
Here the form slot for input
|
|
50
|
+
<br />
|
|
51
|
+
<BmsInputText v-model="args.inputValue"
|
|
52
|
+
label="Mon premier champ de formulaire"
|
|
53
|
+
helper-text="help me here !"
|
|
54
|
+
placeholder="ici votre placeholder"
|
|
55
|
+
/>
|
|
56
|
+
</template>
|
|
57
|
+
<template #form_2>
|
|
58
|
+
<BmsAutocomplete v-model="args.selectValue"
|
|
59
|
+
label="Mon second champ de formulaire"
|
|
60
|
+
placeholder="ici votre placeholder"
|
|
61
|
+
helper-text="help me here !"
|
|
62
|
+
:options="['option1','option2']" />
|
|
63
|
+
</template>
|
|
64
|
+
<template #actions>
|
|
65
|
+
<BmsButton :type="'secondary'">
|
|
66
|
+
Annuler
|
|
67
|
+
</BmsButton>
|
|
68
|
+
<BmsButton :type="'primary'">
|
|
69
|
+
Modifier
|
|
70
|
+
</BmsButton>
|
|
71
|
+
</template>
|
|
72
|
+
</BmsForm>
|
|
73
|
+
</template>
|
|
74
|
+
</BmsContentPageLayout>
|
|
75
|
+
`,
|
|
76
76
|
});
|
|
77
77
|
|
|
78
78
|
export const Default = Template.bind({});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import FieldDatalist, { type Props } from '@/plugins/field/FieldDatalist.vue';
|
|
2
|
+
import { mount } from '@vue/test-utils';
|
|
3
|
+
import { field } from '@/plugins/field';
|
|
4
|
+
|
|
5
|
+
const factory = (props?: Props) => {
|
|
6
|
+
const wrapper = mount(FieldDatalist, {
|
|
7
|
+
global: {
|
|
8
|
+
plugins: [field],
|
|
9
|
+
},
|
|
10
|
+
props: {
|
|
11
|
+
isInputFocused: false,
|
|
12
|
+
options: [
|
|
13
|
+
{ label: 'titi', value: 'i' },
|
|
14
|
+
{ label: 'toto', value: 'o' },
|
|
15
|
+
{ label: 'tutu', value: 'u' },
|
|
16
|
+
],
|
|
17
|
+
modelValue: '',
|
|
18
|
+
...props,
|
|
19
|
+
},
|
|
20
|
+
attachTo: document.body,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
return { wrapper };
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
describe('FieldDatalist', () => {
|
|
27
|
+
it('should be able to select with keyboard', async () => {
|
|
28
|
+
const { wrapper } = factory();
|
|
29
|
+
const titi = wrapper.get('[data-testid="i"]');
|
|
30
|
+
|
|
31
|
+
expect(titi.classes()).toStrictEqual([]);
|
|
32
|
+
await wrapper.trigger('keydown.down');
|
|
33
|
+
expect(titi.classes()).toStrictEqual(['selected']);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -33,3 +33,13 @@ WithSelectedItem.args = {
|
|
|
33
33
|
value: i,
|
|
34
34
|
})),
|
|
35
35
|
};
|
|
36
|
+
|
|
37
|
+
export const CanAddNewOption = Template.bind({});
|
|
38
|
+
CanAddNewOption.args = {
|
|
39
|
+
options: ['toto', 'titi', 'tutu', 'tirlitititututatoooo'].map((i) => ({
|
|
40
|
+
label: i,
|
|
41
|
+
value: i,
|
|
42
|
+
})),
|
|
43
|
+
canAddNewOption: true,
|
|
44
|
+
newOption: 'manual input',
|
|
45
|
+
};
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<ul
|
|
2
|
+
<ul
|
|
3
|
+
class="options-list"
|
|
4
|
+
data-testid="select-options"
|
|
5
|
+
@keydown.up="keyUp"
|
|
6
|
+
@keydown.down="keyDown"
|
|
7
|
+
@keydown.enter="keyEnter"
|
|
8
|
+
>
|
|
3
9
|
<li
|
|
4
10
|
v-for="(option, index) in options"
|
|
5
11
|
:key="index"
|
|
@@ -7,24 +13,96 @@
|
|
|
7
13
|
:class="{
|
|
8
14
|
selected: index === currentSelectedItemIndex,
|
|
9
15
|
}"
|
|
10
|
-
@click.prevent="(
|
|
16
|
+
@click.prevent="onClick(option)"
|
|
11
17
|
>
|
|
12
18
|
<slot name="option" :option="option">
|
|
13
19
|
{{ option.label === null ? 'N/A' : option.label }}
|
|
14
20
|
</slot>
|
|
15
21
|
</li>
|
|
22
|
+
<li
|
|
23
|
+
v-if="displayNewOption"
|
|
24
|
+
@click.prevent="$emits('addNewOption', newOption)"
|
|
25
|
+
>
|
|
26
|
+
<slot name="new-option"> {{ newOption }} (nouvelle option) </slot>
|
|
27
|
+
</li>
|
|
16
28
|
</ul>
|
|
17
29
|
</template>
|
|
18
30
|
|
|
19
31
|
<script setup lang="ts">
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
32
|
+
import { InputOption } from '@/models';
|
|
33
|
+
import { computed, onMounted, ref } from 'vue';
|
|
34
|
+
|
|
35
|
+
export interface Props {
|
|
36
|
+
options: InputOption[];
|
|
37
|
+
isInputFocused: boolean;
|
|
38
|
+
canAddNewOption?: boolean;
|
|
39
|
+
newOption?: string;
|
|
23
40
|
}
|
|
24
41
|
|
|
25
|
-
withDefaults(defineProps<Props>(), {
|
|
42
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
43
|
+
canAddNewOption: false,
|
|
44
|
+
newOption: '',
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const currentSelectedItemIndex = ref<number>(-1);
|
|
48
|
+
|
|
49
|
+
const $emits = defineEmits<{
|
|
50
|
+
select: [option: any];
|
|
51
|
+
addNewOption: [option: string];
|
|
52
|
+
}>();
|
|
53
|
+
|
|
54
|
+
const keyDownEventListener = (ev: KeyboardEvent) => {
|
|
55
|
+
if (props.isInputFocused) {
|
|
56
|
+
switch (ev.key) {
|
|
57
|
+
case 'ArrowDown':
|
|
58
|
+
return keyDown();
|
|
59
|
+
case 'ArrowUp':
|
|
60
|
+
return keyUp();
|
|
61
|
+
case 'Enter':
|
|
62
|
+
return keyEnter();
|
|
63
|
+
default:
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
onMounted(() => {
|
|
70
|
+
document.addEventListener('keydown', keyDownEventListener);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const keyDown = () => {
|
|
74
|
+
if (currentSelectedItemIndex.value < props.options.length - 1) {
|
|
75
|
+
currentSelectedItemIndex.value++;
|
|
76
|
+
} else {
|
|
77
|
+
currentSelectedItemIndex.value = 0;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const keyUp = () => {
|
|
82
|
+
if (currentSelectedItemIndex.value > 0) {
|
|
83
|
+
currentSelectedItemIndex.value--;
|
|
84
|
+
} else {
|
|
85
|
+
currentSelectedItemIndex.value = props.options.length - 1;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const keyEnter = () => {
|
|
90
|
+
if (currentSelectedItemIndex.value >= 0) {
|
|
91
|
+
$emits('select', props.options[currentSelectedItemIndex.value]);
|
|
92
|
+
currentSelectedItemIndex.value = -1;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const onClick = (option: { label: string; value: any }) => {
|
|
97
|
+
$emits('select', option);
|
|
98
|
+
};
|
|
26
99
|
|
|
27
|
-
const
|
|
100
|
+
const displayNewOption = computed(
|
|
101
|
+
() =>
|
|
102
|
+
props.canAddNewOption &&
|
|
103
|
+
props.newOption &&
|
|
104
|
+
!props.options.find((o) => o.value === props.newOption),
|
|
105
|
+
);
|
|
28
106
|
</script>
|
|
29
107
|
|
|
30
108
|
<style lang="scss" scoped>
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
label="Autocomplete avec label/value"
|
|
5
5
|
:options="optionsLabelValue"
|
|
6
6
|
v-model="inputLabelValue"
|
|
7
|
+
:can-add-new-option="true"
|
|
8
|
+
@add-new-option="onAddNewOption"
|
|
7
9
|
/>
|
|
8
10
|
|
|
9
11
|
Valeur: {{ inputLabelValue }}
|
|
@@ -12,6 +14,7 @@
|
|
|
12
14
|
label="Autocomplete avec icones"
|
|
13
15
|
:options="optionsIcon"
|
|
14
16
|
v-model="inputValueIcon"
|
|
17
|
+
@select="(o) => console.log('select', o.value)"
|
|
15
18
|
/>
|
|
16
19
|
Valeur: {{ inputValueIcon }}
|
|
17
20
|
|
|
@@ -51,4 +54,8 @@ const optionsIcon = ref(
|
|
|
51
54
|
),
|
|
52
55
|
);
|
|
53
56
|
const inputValueIcon = ref('');
|
|
57
|
+
|
|
58
|
+
const onAddNewOption = (newOption: string) => {
|
|
59
|
+
optionsLabelValue.value.push({ label: newOption, value: newOption });
|
|
60
|
+
};
|
|
54
61
|
</script>
|
|
@@ -322,7 +322,7 @@ import BmsIconButton from '@/components/button/BmsIconButton.vue';
|
|
|
322
322
|
|
|
323
323
|
const { success } = useNotifications();
|
|
324
324
|
const text = ref('');
|
|
325
|
-
const selected = ref('');
|
|
325
|
+
const selected = ref('value1');
|
|
326
326
|
const selected2 = ref('');
|
|
327
327
|
const files: Ref<File[]> = ref([]);
|
|
328
328
|
const selectedRadio = ref('titi');
|