@ouestfrance/sipa-bms-ui 8.16.0 → 8.18.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/dist/components/navigation/BmsTabs.vue.d.ts +6 -1
- package/dist/components/navigation/UiTab.vue.d.ts +5 -4
- package/dist/components/navigation/UiTabs.vue.d.ts +21 -0
- package/dist/components/table/BmsTable.vue.d.ts +3 -1
- package/dist/components/table/UiBmsTable.vue.d.ts +1 -1
- package/dist/helpers/tab.helper.d.ts +2 -0
- package/dist/helpers/table.helper.d.ts +1 -0
- package/dist/models/tab.model.d.ts +5 -0
- package/dist/models/table.model.d.ts +2 -1
- package/dist/sipa-bms-ui.css +70 -69
- package/dist/sipa-bms-ui.es.js +380 -294
- package/dist/sipa-bms-ui.es.js.map +1 -1
- package/dist/sipa-bms-ui.umd.js +388 -301
- package/dist/sipa-bms-ui.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/components/layout/BmsFloatingWindow.vue +1 -0
- package/src/components/navigation/BmsTabs.stories.js +58 -4
- package/src/components/navigation/BmsTabs.vue +53 -33
- package/src/components/navigation/UiTab.stories.js +5 -5
- package/src/components/navigation/UiTab.vue +15 -20
- package/src/components/navigation/UiTabs.stories.js +34 -0
- package/src/components/navigation/UiTabs.vue +76 -0
- package/src/components/table/BmsTable.stories.js +47 -0
- package/src/components/table/BmsTable.vue +22 -3
- package/src/components/table/UiBmsTable.spec.ts +47 -3
- package/src/components/table/UiBmsTable.stories.js +43 -0
- package/src/components/table/UiBmsTable.vue +51 -91
- package/src/components/table/UiBmsTableRow.vue +18 -3
- package/src/documentation/principles.mdx +73 -7
- package/src/helpers/tab.helper.ts +3 -0
- package/src/helpers/table.helper.ts +19 -0
- package/src/models/tab.model.ts +6 -0
- package/src/models/table.model.ts +1 -0
- package/src/showroom/pages/server-table.vue +1 -4
- package/src/showroom/pages/table.vue +3 -0
package/package.json
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import BmsTabs from '@/components/navigation/BmsTabs.vue';
|
|
2
|
+
import { vueRouter } from 'storybook-vue3-router';
|
|
2
3
|
|
|
3
4
|
export default {
|
|
4
5
|
title: 'Composants/navigation/Tabs',
|
|
@@ -6,6 +7,21 @@ export default {
|
|
|
6
7
|
argTypes: {
|
|
7
8
|
items: {},
|
|
8
9
|
},
|
|
10
|
+
decorators: [
|
|
11
|
+
vueRouter([
|
|
12
|
+
{
|
|
13
|
+
name: 'titi',
|
|
14
|
+
path: '/titi',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
path: '/',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: 'toto',
|
|
21
|
+
path: '/toto',
|
|
22
|
+
},
|
|
23
|
+
]),
|
|
24
|
+
],
|
|
9
25
|
};
|
|
10
26
|
|
|
11
27
|
const Template = (args) => ({
|
|
@@ -20,11 +36,49 @@ const Template = (args) => ({
|
|
|
20
36
|
`,
|
|
21
37
|
});
|
|
22
38
|
|
|
23
|
-
export const
|
|
24
|
-
|
|
39
|
+
export const Default = Template.bind({});
|
|
40
|
+
Default.args = {
|
|
41
|
+
title: 'Title',
|
|
42
|
+
tabs: [{ name: 'Titi', id: 'titi' }, { name: 'Toto' }],
|
|
43
|
+
};
|
|
44
|
+
export const WithSelectedTabId = Template.bind({});
|
|
45
|
+
WithSelectedTabId.args = {
|
|
46
|
+
title: 'Title',
|
|
47
|
+
initialTabId: 'Toto',
|
|
48
|
+
tabs: [
|
|
49
|
+
{ name: 'Titi', id: 'titi' },
|
|
50
|
+
{ name: 'Tata' },
|
|
51
|
+
{ name: 'Toto' },
|
|
52
|
+
{ name: 'Tutu' },
|
|
53
|
+
],
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const WithRouterEngine = Template.bind({});
|
|
57
|
+
WithRouterEngine.args = {
|
|
58
|
+
title: 'Title',
|
|
59
|
+
initialTabId: 'titi',
|
|
60
|
+
tabs: [
|
|
61
|
+
{ name: 'Titi', routeName: 'titi', id: 'titi' },
|
|
62
|
+
{ name: 'Toto', routePath: '/toto' },
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const WithError = Template.bind({});
|
|
67
|
+
WithError.args = {
|
|
68
|
+
title: 'Title',
|
|
69
|
+
initialTabId: 'titi',
|
|
70
|
+
tabs: [
|
|
71
|
+
{ name: 'Titi', routeName: 'titi', id: 'titi' },
|
|
72
|
+
{ name: 'Toto', routePath: '/toto', error: true },
|
|
73
|
+
],
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export const WithDisabled = Template.bind({});
|
|
77
|
+
WithDisabled.args = {
|
|
25
78
|
title: 'Title',
|
|
79
|
+
initialTabId: 'titi',
|
|
26
80
|
tabs: [
|
|
27
|
-
{ name: 'Titi',
|
|
28
|
-
{ name: 'Toto', routePath: 'toto' },
|
|
81
|
+
{ name: 'Titi', routeName: 'titi', id: 'titi' },
|
|
82
|
+
{ name: 'Toto', routePath: '/toto', disabled: true },
|
|
29
83
|
],
|
|
30
84
|
};
|
|
@@ -1,46 +1,66 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
:
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
2
|
+
<UiTabs
|
|
3
|
+
:title="title"
|
|
4
|
+
:tabs="tabs"
|
|
5
|
+
:initial-tab-id="selectedTabId"
|
|
6
|
+
@click="$emits('click', $event)"
|
|
7
|
+
>
|
|
8
|
+
<template v-if="needRouterEngine" #router="{ tab }">
|
|
9
|
+
<router-link class="tab" :to="getTabTarget(tab)">{{
|
|
10
|
+
tab.name
|
|
11
|
+
}}</router-link>
|
|
12
|
+
</template>
|
|
13
|
+
</UiTabs>
|
|
13
14
|
</template>
|
|
14
15
|
|
|
15
16
|
<script setup lang="ts">
|
|
16
|
-
import { useRouter } from 'vue-router';
|
|
17
17
|
import { Tab } from '@/models/tab.model';
|
|
18
|
-
import
|
|
18
|
+
import UiTabs from './UiTabs.vue';
|
|
19
|
+
import { computed, ComputedRef, onMounted, ref, watch } from 'vue';
|
|
20
|
+
import { RouteLocation, useRouter } from 'vue-router';
|
|
21
|
+
import { getTabId } from '@/helpers/tab.helper';
|
|
19
22
|
|
|
20
23
|
const { currentRoute } = useRouter();
|
|
21
24
|
|
|
22
|
-
defineProps<{
|
|
23
|
-
|
|
25
|
+
const props = defineProps<{
|
|
26
|
+
title: string;
|
|
27
|
+
tabs: Tab[];
|
|
28
|
+
initialTabId?: string;
|
|
29
|
+
}>();
|
|
24
30
|
|
|
25
|
-
|
|
26
|
-
.
|
|
27
|
-
|
|
28
|
-
align-items: center;
|
|
29
|
-
border-bottom: 1px solid var(--bms-grey-25);
|
|
30
|
-
box-sizing: border-box;
|
|
31
|
+
const getTabTarget = (tab: Tab) => {
|
|
32
|
+
return tab.routePath ? { path: tab.routePath } : { name: tab.routeName };
|
|
33
|
+
};
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
const $emits = defineEmits<{
|
|
36
|
+
(e: 'click', tab: Tab): void;
|
|
37
|
+
}>();
|
|
35
38
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
const needRouterEngine = computed(() => {
|
|
40
|
+
return (
|
|
41
|
+
!!props.tabs &&
|
|
42
|
+
!!props.tabs[0] &&
|
|
43
|
+
!!(props.tabs[0].routePath || props.tabs[0].routeName)
|
|
44
|
+
);
|
|
45
|
+
});
|
|
40
46
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
47
|
+
const selectedTabId: ComputedRef<string | null> = computed(() => {
|
|
48
|
+
if (needRouterEngine.value) {
|
|
49
|
+
const selectedTab =
|
|
50
|
+
props.tabs.find((t) => isTabSelected(t, currentRoute.value)) || null;
|
|
51
|
+
return selectedTab ? getTabId(selectedTab) : null;
|
|
52
|
+
} else if (props.initialTabId) {
|
|
53
|
+
return props.initialTabId;
|
|
54
|
+
} else {
|
|
55
|
+
return null;
|
|
44
56
|
}
|
|
45
|
-
}
|
|
46
|
-
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const isTabSelected = (tab: Tab, currentRoute: RouteLocation) =>
|
|
60
|
+
tab.routePath
|
|
61
|
+
? currentRoute.path.includes(tab.routePath)
|
|
62
|
+
: tab.routeName
|
|
63
|
+
? !!currentRoute.name &&
|
|
64
|
+
(currentRoute.name as string).includes(tab.routeName)
|
|
65
|
+
: false;
|
|
66
|
+
</script>
|
|
@@ -30,31 +30,31 @@ const Template = (args) => ({
|
|
|
30
30
|
|
|
31
31
|
export const Default = Template.bind({});
|
|
32
32
|
Default.args = {
|
|
33
|
-
|
|
33
|
+
selectedTabId: 'toto',
|
|
34
34
|
tab: { name: 'Titi', routePath: 'titi' },
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
export const Active = Template.bind({});
|
|
38
38
|
Active.args = {
|
|
39
|
-
|
|
39
|
+
selectedTabId: 'Titi',
|
|
40
40
|
tab: { name: 'Titi', routePath: 'titi' },
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
export const Disabled = Template.bind({});
|
|
44
44
|
Disabled.args = {
|
|
45
|
-
|
|
45
|
+
selectedTabId: 'toto',
|
|
46
46
|
tab: { name: 'Titi', routePath: 'titi', disabled: true },
|
|
47
47
|
};
|
|
48
48
|
|
|
49
49
|
export const Error = Template.bind({});
|
|
50
50
|
Error.args = {
|
|
51
|
-
|
|
51
|
+
selectedTabId: 'toto',
|
|
52
52
|
tab: { name: 'Titi', routePath: 'titi', error: true },
|
|
53
53
|
};
|
|
54
54
|
|
|
55
55
|
export const Hover = Template.bind({});
|
|
56
56
|
Hover.args = {
|
|
57
|
-
|
|
57
|
+
selectedTabId: 'toto',
|
|
58
58
|
tab: { name: 'Titi', routePath: 'titi' },
|
|
59
59
|
};
|
|
60
60
|
Hover.play = async ({ canvasElement }) => {
|
|
@@ -1,40 +1,30 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
:to="tabTarget"
|
|
2
|
+
<span
|
|
4
3
|
data-testid="tab"
|
|
5
4
|
class="tab"
|
|
6
5
|
:class="{ active: isTabSelected, error: tab.error, disabled: tab.disabled }"
|
|
7
6
|
>
|
|
8
|
-
<slot>{{ tab.name }}</slot>
|
|
9
|
-
</
|
|
7
|
+
<slot name="router" :tab="tab">{{ tab.name }}</slot>
|
|
8
|
+
</span>
|
|
10
9
|
</template>
|
|
11
10
|
|
|
12
11
|
<script lang="ts" setup>
|
|
13
12
|
import { Tab } from '@/models/tab.model';
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
13
|
+
import { computed, ComputedRef } from 'vue';
|
|
14
|
+
import { getTabId } from '@/helpers/tab.helper';
|
|
16
15
|
|
|
17
16
|
const props = withDefaults(
|
|
18
17
|
defineProps<{
|
|
19
|
-
|
|
18
|
+
selectedTabId: string | null;
|
|
20
19
|
tab: Tab;
|
|
21
20
|
}>(),
|
|
22
21
|
{},
|
|
23
22
|
);
|
|
24
23
|
|
|
25
|
-
const
|
|
26
|
-
props.tab.routePath
|
|
27
|
-
? props.currentRoute.path.includes(props.tab.routePath)
|
|
28
|
-
: props.tab.routeName
|
|
29
|
-
? !!props.currentRoute.name &&
|
|
30
|
-
(props.currentRoute.name as string).includes(props.tab.routeName)
|
|
31
|
-
: false,
|
|
32
|
-
);
|
|
24
|
+
const tabId = computed(() => getTabId(props.tab));
|
|
33
25
|
|
|
34
|
-
const
|
|
35
|
-
props.
|
|
36
|
-
? { path: props.tab.routePath }
|
|
37
|
-
: { name: props.tab.routeName },
|
|
26
|
+
const isTabSelected: ComputedRef<boolean> = computed(
|
|
27
|
+
() => (tabId.value && tabId.value === props.selectedTabId) || false,
|
|
38
28
|
);
|
|
39
29
|
</script>
|
|
40
30
|
|
|
@@ -47,10 +37,15 @@ const tabTarget: ComputedRef<RouteLocationRaw> = computed(() =>
|
|
|
47
37
|
text-decoration: none;
|
|
48
38
|
border-bottom: 4px solid var(--tab-border-color);
|
|
49
39
|
padding: 0 8px 16px 8px;
|
|
50
|
-
|
|
40
|
+
cursor: pointer;
|
|
41
|
+
:deep(a) {
|
|
42
|
+
color: var(--tab-color);
|
|
43
|
+
text-decoration: none;
|
|
44
|
+
}
|
|
51
45
|
&:hover,
|
|
52
46
|
&__hover {
|
|
53
47
|
--tab-border-color: var(--bms-main-50);
|
|
48
|
+
text-decoration: none;
|
|
54
49
|
}
|
|
55
50
|
|
|
56
51
|
&.active {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import UiTabs from '@/components/navigation/UiTabs.vue';
|
|
2
|
+
import template from '@/documentation/template_field_dependency.mdx';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
parameters: {
|
|
6
|
+
docs: {
|
|
7
|
+
page: template,
|
|
8
|
+
},
|
|
9
|
+
},
|
|
10
|
+
title: 'Composants/navigation/UiTabs',
|
|
11
|
+
component: UiTabs,
|
|
12
|
+
argTypes: {
|
|
13
|
+
items: {},
|
|
14
|
+
},
|
|
15
|
+
tags: ['technical'],
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const Template = (args) => ({
|
|
19
|
+
components: {
|
|
20
|
+
UiTabs,
|
|
21
|
+
},
|
|
22
|
+
setup() {
|
|
23
|
+
return { args };
|
|
24
|
+
},
|
|
25
|
+
template: `
|
|
26
|
+
<UiTabs v-bind="args" />
|
|
27
|
+
`,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export const Primary = Template.bind({});
|
|
31
|
+
Primary.args = {
|
|
32
|
+
title: 'Title',
|
|
33
|
+
tabs: [{ name: 'Titi', id: 'titi' }, { name: 'Toto' }],
|
|
34
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="tabs-header">
|
|
3
|
+
<div class="tabs-title">
|
|
4
|
+
<h3>{{ title }}</h3>
|
|
5
|
+
</div>
|
|
6
|
+
<UiTab
|
|
7
|
+
v-for="tab in tabs"
|
|
8
|
+
:key="getTabId(tab)"
|
|
9
|
+
:selectedTabId="selectedTabId"
|
|
10
|
+
:tab="tab"
|
|
11
|
+
@click="onTabClick(tab)"
|
|
12
|
+
>
|
|
13
|
+
<template #router="{ tab }"><slot name="router" :tab="tab" /></template>
|
|
14
|
+
</UiTab>
|
|
15
|
+
</div>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<script setup lang="ts">
|
|
19
|
+
import { Tab } from '@/models/tab.model';
|
|
20
|
+
import UiTab from './UiTab.vue';
|
|
21
|
+
import { computed, ComputedRef, onMounted, ref, watch } from 'vue';
|
|
22
|
+
import { getTabId } from '@/helpers/tab.helper';
|
|
23
|
+
|
|
24
|
+
const props = defineProps<{
|
|
25
|
+
title: string;
|
|
26
|
+
tabs: Tab[];
|
|
27
|
+
initialTabId?: string | null;
|
|
28
|
+
}>();
|
|
29
|
+
|
|
30
|
+
const selectedTabId = ref<string | null>(null);
|
|
31
|
+
|
|
32
|
+
onMounted(() => {
|
|
33
|
+
selectedTabId.value = props.initialTabId || null;
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
watch(
|
|
37
|
+
() => props.initialTabId,
|
|
38
|
+
() => {
|
|
39
|
+
selectedTabId.value = props.initialTabId || null;
|
|
40
|
+
},
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const $emits = defineEmits<{
|
|
44
|
+
(e: 'click', value: any): void;
|
|
45
|
+
}>();
|
|
46
|
+
|
|
47
|
+
const onTabClick = (tab: Tab) => {
|
|
48
|
+
if (getTabId(tab) !== selectedTabId.value) {
|
|
49
|
+
$emits('click', tab);
|
|
50
|
+
selectedTabId.value = getTabId(tab);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
</script>
|
|
54
|
+
|
|
55
|
+
<style lang="scss" scoped>
|
|
56
|
+
.tabs-header {
|
|
57
|
+
display: flex;
|
|
58
|
+
align-items: center;
|
|
59
|
+
border-bottom: 1px solid var(--bms-grey-25);
|
|
60
|
+
box-sizing: border-box;
|
|
61
|
+
|
|
62
|
+
.tabs-title {
|
|
63
|
+
margin-right: auto;
|
|
64
|
+
margin-bottom: 16px;
|
|
65
|
+
|
|
66
|
+
h3 {
|
|
67
|
+
margin: 0;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
span {
|
|
72
|
+
position: relative;
|
|
73
|
+
top: 1px;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
</style>
|
|
@@ -10,6 +10,12 @@ export default {
|
|
|
10
10
|
disableSearch: {
|
|
11
11
|
control: { type: 'boolean' },
|
|
12
12
|
},
|
|
13
|
+
selectMode: {
|
|
14
|
+
options: ['single', 'default'],
|
|
15
|
+
},
|
|
16
|
+
mode: {
|
|
17
|
+
options: ['normal', 'dense'],
|
|
18
|
+
},
|
|
13
19
|
loading: {
|
|
14
20
|
control: { type: 'boolean' },
|
|
15
21
|
},
|
|
@@ -751,6 +757,47 @@ Selected.args = {
|
|
|
751
757
|
selectable: true,
|
|
752
758
|
};
|
|
753
759
|
|
|
760
|
+
export const SingleSelected = Template.bind({});
|
|
761
|
+
SingleSelected.args = {
|
|
762
|
+
headers: [
|
|
763
|
+
{
|
|
764
|
+
label: 'Column 1',
|
|
765
|
+
key: 'col1',
|
|
766
|
+
align: 'start',
|
|
767
|
+
},
|
|
768
|
+
{
|
|
769
|
+
label: 'Column 2',
|
|
770
|
+
key: 'col2',
|
|
771
|
+
align: 'center',
|
|
772
|
+
},
|
|
773
|
+
{
|
|
774
|
+
label: 'Column 3',
|
|
775
|
+
key: 'col3',
|
|
776
|
+
align: 'end',
|
|
777
|
+
},
|
|
778
|
+
],
|
|
779
|
+
items: [
|
|
780
|
+
{
|
|
781
|
+
col1: 'Value1',
|
|
782
|
+
col2: 'Value2',
|
|
783
|
+
col3: 'Value3',
|
|
784
|
+
},
|
|
785
|
+
{
|
|
786
|
+
col1: 'Value4',
|
|
787
|
+
col2: 'Value5',
|
|
788
|
+
col3: 'Value6',
|
|
789
|
+
},
|
|
790
|
+
],
|
|
791
|
+
selectedItems: [
|
|
792
|
+
{
|
|
793
|
+
col1: 'Value1',
|
|
794
|
+
col2: 'Value2',
|
|
795
|
+
col3: 'Value3',
|
|
796
|
+
},
|
|
797
|
+
],
|
|
798
|
+
selectable: true,
|
|
799
|
+
selectMode: 'single',
|
|
800
|
+
};
|
|
754
801
|
const TemplateWithSelectAction = (args) => ({
|
|
755
802
|
components: {
|
|
756
803
|
BmsTable,
|
|
@@ -6,7 +6,14 @@ import UiFilterButton from '@/components/table/UiFilterButton.vue';
|
|
|
6
6
|
import { usePagination } from '@/composables/pagination.composable';
|
|
7
7
|
import { useSearch } from '@/composables/search.composable';
|
|
8
8
|
import { useSort } from '@/composables/sort.composable';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
Filter,
|
|
11
|
+
SavedFilter,
|
|
12
|
+
SelectMode,
|
|
13
|
+
Sort,
|
|
14
|
+
SortValue,
|
|
15
|
+
TableHeader,
|
|
16
|
+
} from '@/models';
|
|
10
17
|
import { computed, Ref, ref, watch, watchEffect } from 'vue';
|
|
11
18
|
import BmsTableFilters from './BmsTableFilters.vue';
|
|
12
19
|
import BmsSearch from '../form/BmsSearch.vue';
|
|
@@ -34,6 +41,7 @@ interface UiTableProps {
|
|
|
34
41
|
defaultSort?: Sort;
|
|
35
42
|
selectable?: boolean;
|
|
36
43
|
selectableDisabled?: boolean;
|
|
44
|
+
selectMode?: SelectMode.DEFAULT | SelectMode.SINGLE;
|
|
37
45
|
}
|
|
38
46
|
|
|
39
47
|
const props = withDefaults(defineProps<UiTableProps>(), {
|
|
@@ -51,6 +59,7 @@ const props = withDefaults(defineProps<UiTableProps>(), {
|
|
|
51
59
|
}),
|
|
52
60
|
selectable: false,
|
|
53
61
|
selectableDisabled: false,
|
|
62
|
+
selectMode: SelectMode.DEFAULT,
|
|
54
63
|
});
|
|
55
64
|
|
|
56
65
|
const {
|
|
@@ -212,9 +221,18 @@ const onClickHeader = (header: TableHeader) => {
|
|
|
212
221
|
changeSort(header);
|
|
213
222
|
};
|
|
214
223
|
|
|
224
|
+
const elementsAndChildElements = computed(() => {
|
|
225
|
+
const childElements = items.value
|
|
226
|
+
.map((item) => item.childElement)
|
|
227
|
+
.filter((childElement) => !!childElement);
|
|
228
|
+
return [...items.value, ...childElements];
|
|
229
|
+
});
|
|
230
|
+
|
|
215
231
|
const onSelectAll = () => {
|
|
216
|
-
selectedItems.value =
|
|
232
|
+
selectedItems.value = elementsAndChildElements.value;
|
|
217
233
|
};
|
|
234
|
+
|
|
235
|
+
const totalSize = computed(() => elementsAndChildElements.value.length);
|
|
218
236
|
</script>
|
|
219
237
|
|
|
220
238
|
<template>
|
|
@@ -228,7 +246,8 @@ const onSelectAll = () => {
|
|
|
228
246
|
:sort="sort"
|
|
229
247
|
:selectable="selectable"
|
|
230
248
|
:selectableDisabled="selectableDisabled"
|
|
231
|
-
:totalSize="
|
|
249
|
+
:totalSize="totalSize"
|
|
250
|
+
:selectMode="selectMode"
|
|
232
251
|
@clickHeader="onClickHeader"
|
|
233
252
|
@selectAll="onSelectAll"
|
|
234
253
|
>
|
|
@@ -2,9 +2,8 @@ import UiBmsTable from '@/components/table/UiBmsTable.vue';
|
|
|
2
2
|
import { field } from '@/plugins/field';
|
|
3
3
|
import { mount, MountingOptions } from '@vue/test-utils';
|
|
4
4
|
import UiBmsInputCheckbox from '../form/UiBmsInputCheckbox.vue';
|
|
5
|
-
import BmsAlert from '../feedback/BmsAlert.vue';
|
|
6
|
-
import { wrap } from 'lodash';
|
|
7
5
|
import { SelectMode } from '@/models';
|
|
6
|
+
import BmsInputRadio from '../form/BmsInputRadio.vue';
|
|
8
7
|
|
|
9
8
|
const intersectionObserverMock = () => ({
|
|
10
9
|
observe: () => null,
|
|
@@ -40,13 +39,21 @@ const factory = (options: MountingOptions<any, {}> = {}) => {
|
|
|
40
39
|
} as any);
|
|
41
40
|
return {
|
|
42
41
|
wrapper,
|
|
42
|
+
selectedMessage: () => wrapper.findAll('.bms-table__selected')[0],
|
|
43
|
+
|
|
44
|
+
getAllCheckboxes: () => wrapper.findAllComponents(UiBmsInputCheckbox),
|
|
43
45
|
getAllCheckedCheckboxes: () =>
|
|
44
46
|
wrapper
|
|
45
47
|
.findAllComponents(UiBmsInputCheckbox)
|
|
46
48
|
.filter((checkbox) => checkbox.props('modelValue') === true),
|
|
47
49
|
getTableCheckbox: () => wrapper.findAllComponents(UiBmsInputCheckbox)[0],
|
|
48
50
|
getFirstRowCheckbox: () => wrapper.findAllComponents(UiBmsInputCheckbox)[1],
|
|
49
|
-
|
|
51
|
+
|
|
52
|
+
getAllCheckedRadio: () =>
|
|
53
|
+
wrapper
|
|
54
|
+
.findAllComponents(BmsInputRadio)
|
|
55
|
+
.filter((checkbox) => checkbox.props('modelValue') === true),
|
|
56
|
+
getAllRadio: () => wrapper.findAllComponents(BmsInputRadio),
|
|
50
57
|
};
|
|
51
58
|
};
|
|
52
59
|
describe('UiBmsTable', () => {
|
|
@@ -369,5 +376,42 @@ describe('UiBmsTable', () => {
|
|
|
369
376
|
'Vous avez sélectionné la totalité des 2 éléments.',
|
|
370
377
|
);
|
|
371
378
|
});
|
|
379
|
+
it('should display readio if in selectMode SINGLE', async () => {
|
|
380
|
+
const { getAllRadio, getAllCheckboxes } = factory({
|
|
381
|
+
props: {
|
|
382
|
+
headers,
|
|
383
|
+
items,
|
|
384
|
+
totalSize: 2,
|
|
385
|
+
selectable: true,
|
|
386
|
+
selectMode: SelectMode.SINGLE,
|
|
387
|
+
selectedItems: [items[0]],
|
|
388
|
+
},
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
expect(getAllRadio().length).toEqual(2);
|
|
392
|
+
expect(getAllCheckboxes().length).toEqual(0);
|
|
393
|
+
});
|
|
394
|
+
it('should unselect previous selection if in selectMode SINGLE', async () => {
|
|
395
|
+
const { getAllRadio } = factory({
|
|
396
|
+
props: {
|
|
397
|
+
headers,
|
|
398
|
+
items,
|
|
399
|
+
totalSize: 2,
|
|
400
|
+
selectable: true,
|
|
401
|
+
selectMode: SelectMode.SINGLE,
|
|
402
|
+
selectedItems: [items[0]],
|
|
403
|
+
},
|
|
404
|
+
});
|
|
405
|
+
const firstRadio = getAllRadio()[0];
|
|
406
|
+
const secondRadio = getAllRadio()[1];
|
|
407
|
+
|
|
408
|
+
expect(firstRadio.props('modelValue')).toBeTruthy();
|
|
409
|
+
expect(secondRadio.props('modelValue')).toBeFalsy();
|
|
410
|
+
|
|
411
|
+
await secondRadio.get('input').setValue(items[1]);
|
|
412
|
+
|
|
413
|
+
expect(secondRadio.props('modelValue')).toBeTruthy();
|
|
414
|
+
expect(firstRadio.props('modelValue')).toBeFalsy();
|
|
415
|
+
});
|
|
372
416
|
});
|
|
373
417
|
});
|
|
@@ -559,6 +559,49 @@ SelectedWithMaxSelected.args = {
|
|
|
559
559
|
maxSelectedSize: 1,
|
|
560
560
|
};
|
|
561
561
|
|
|
562
|
+
export const SingleSelect = Template.bind({});
|
|
563
|
+
SingleSelect.args = {
|
|
564
|
+
headers: [
|
|
565
|
+
{
|
|
566
|
+
label: 'Column 1',
|
|
567
|
+
key: 'col1',
|
|
568
|
+
align: 'start',
|
|
569
|
+
},
|
|
570
|
+
{
|
|
571
|
+
label: 'Column 2',
|
|
572
|
+
key: 'col2',
|
|
573
|
+
align: 'center',
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
label: 'Column 3',
|
|
577
|
+
key: 'col3',
|
|
578
|
+
align: 'end',
|
|
579
|
+
},
|
|
580
|
+
],
|
|
581
|
+
items: [
|
|
582
|
+
{
|
|
583
|
+
col1: 'Value1',
|
|
584
|
+
col2: 'Value2',
|
|
585
|
+
col3: 'Value3',
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
col1: 'Value4',
|
|
589
|
+
col2: 'Value5',
|
|
590
|
+
col3: 'Value6',
|
|
591
|
+
},
|
|
592
|
+
],
|
|
593
|
+
selectedItems: [
|
|
594
|
+
{
|
|
595
|
+
col1: 'Value1',
|
|
596
|
+
col2: 'Value2',
|
|
597
|
+
col3: 'Value3',
|
|
598
|
+
},
|
|
599
|
+
],
|
|
600
|
+
selectable: true,
|
|
601
|
+
selectMode: 'single',
|
|
602
|
+
totalSize: 2,
|
|
603
|
+
};
|
|
604
|
+
|
|
562
605
|
export const AllSelected = Template.bind({});
|
|
563
606
|
AllSelected.args = {
|
|
564
607
|
headers: [
|