@ouestfrance/sipa-bms-ui 8.10.0 → 8.11.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.
Files changed (56) hide show
  1. package/dist/components/button/BmsButton.vue.d.ts +2 -1
  2. package/dist/components/button/BmsIconButton.vue.d.ts +3 -1
  3. package/dist/components/button/UiButton.vue.d.ts +4 -2
  4. package/dist/components/button/UiButtonLink.vue.d.ts +1 -0
  5. package/dist/components/feedback/BmsTooltip.vue.d.ts +1 -1
  6. package/dist/components/feedback/UiTooltip.vue.d.ts +1 -1
  7. package/dist/components/form/BmsInputText.vue.d.ts +2 -0
  8. package/dist/components/form/BmsSearch.vue.d.ts +4 -0
  9. package/dist/components/form/RawAutocomplete.vue.d.ts +2 -0
  10. package/dist/components/layout/BmsHeaderTitle.vue.d.ts +1 -1
  11. package/dist/components/layout/UiPopoverMenu.vue.d.ts +1 -1
  12. package/dist/components/navigation/BmsBreadcrumb.vue.d.ts +1 -1
  13. package/dist/components/navigation/BmsShortLinkMenu.vue.d.ts +2 -2
  14. package/dist/components/navigation/UiTenantSwitcher.vue.d.ts +4 -0
  15. package/dist/components/table/BmsTableFilters.vue.d.ts +4 -0
  16. package/dist/components/utils/BmsCocarde.vue.d.ts +1 -1
  17. package/dist/mockServiceWorker.js +2 -1
  18. package/dist/plugins/field/FieldComponent.vue.d.ts +3 -0
  19. package/dist/plugins/field/SkeletonField.vue.d.ts +2 -0
  20. package/dist/plugins/field/field-component.model.d.ts +2 -0
  21. package/dist/plugins/router-history/router-history.composable.d.ts +1 -1
  22. package/dist/sipa-bms-ui.css +105 -65
  23. package/dist/sipa-bms-ui.es.js +4099 -4008
  24. package/dist/sipa-bms-ui.es.js.map +1 -1
  25. package/dist/sipa-bms-ui.umd.js +4106 -4015
  26. package/dist/sipa-bms-ui.umd.js.map +1 -1
  27. package/package.json +19 -18
  28. package/src/components/button/BmsButton.stories.js +26 -0
  29. package/src/components/button/BmsButton.vue +2 -0
  30. package/src/components/button/BmsIconButton.stories.js +4 -0
  31. package/src/components/button/BmsIconButton.vue +10 -1
  32. package/src/components/button/UiButton.vue +2 -0
  33. package/src/components/button/UiButtonLink.vue +13 -6
  34. package/src/components/feedback/UiTooltip.vue +1 -0
  35. package/src/components/form/BmsAutocomplete.stories.js +12 -0
  36. package/src/components/form/BmsAutocomplete.vue +1 -0
  37. package/src/components/form/BmsInputText.stories.js +8 -0
  38. package/src/components/form/BmsInputToggle.stories.js +27 -0
  39. package/src/components/form/BmsInputToggle.vue +16 -2
  40. package/src/components/form/BmsMultiSelect.vue +6 -1
  41. package/src/components/form/BmsSelect.stories.js +9 -0
  42. package/src/components/form/BmsSelect.vue +4 -2
  43. package/src/components/form/BmsServerAutocomplete.stories.js +25 -2
  44. package/src/components/form/BmsServerAutocomplete.vue +1 -0
  45. package/src/components/form/BmsTextArea.vue +1 -1
  46. package/src/components/form/UiBmsSwitch.vue +1 -0
  47. package/src/components/layout/BmsModal.stories.js +1 -1
  48. package/src/components/table/BmsServerTable.vue +2 -4
  49. package/src/plugins/field/FieldComponent.spec.ts +2 -1
  50. package/src/plugins/field/FieldComponent.stories.js +18 -1
  51. package/src/plugins/field/FieldComponent.vue +24 -4
  52. package/src/plugins/field/SkeletonField.stories.js +21 -0
  53. package/src/plugins/field/SkeletonField.vue +27 -0
  54. package/src/plugins/field/field-component.model.ts +2 -0
  55. package/src/showroom/App.vue +5 -0
  56. package/src/showroom/pages/loading-form.vue +78 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouestfrance/sipa-bms-ui",
3
- "version": "8.10.0",
3
+ "version": "8.11.0",
4
4
  "author": "Ouest-France BMS",
5
5
  "license": "ISC",
6
6
  "scripts": {
@@ -15,7 +15,8 @@
15
15
  "test:e2e:headless": "START_SERVER_AND_TEST_INSECURE=1 start-server-and-test serve http://localhost:8080 'cypress run'",
16
16
  "test:e2e:open": "START_SERVER_AND_TEST_INSECURE=1 start-server-and-test dev:showroom https://localhost:1234 'cypress open'",
17
17
  "storybook:dev": "storybook dev -p 6006 --no-open",
18
- "storybook:build": "storybook build --loglevel warn --quiet",
18
+ "storybook:build": "storybook build --loglevel warn --quiet --stats-json",
19
+ "chromatic:turbosnap": "CHROMATIC_PROJECT_TOKEN=chpt_4854db5bc2ca640 chromatic --allow-console-errors --storybook-build-dir=storybook-static --only-changed",
19
20
  "chromatic": "CHROMATIC_PROJECT_TOKEN=chpt_4854db5bc2ca640 chromatic --allow-console-errors --storybook-build-dir=storybook-static",
20
21
  "chromatic:dev": "npm run storybook:build && npm run chromatic",
21
22
  "chromatic:accept": "npm run chromatic -- --auto-accept-changes",
@@ -30,15 +31,15 @@
30
31
  },
31
32
  "devDependencies": {
32
33
  "@chromatic-com/storybook": "^4.0.0",
33
- "@codemirror/lang-html": "6.4.10",
34
+ "@codemirror/lang-html": "6.4.11",
34
35
  "@codemirror/lang-json": "6.0.2",
35
36
  "@commitlint/cli": "19.8.1",
36
37
  "@commitlint/config-conventional": "19.8.1",
37
38
  "@formkit/vue": "1.6.9",
38
39
  "@mdx-js/react": "3.1.1",
39
- "@storybook/addon-docs": "9.1.7",
40
- "@storybook/addon-links": "9.1.7",
41
- "@storybook/vue3-vite": "9.1.7",
40
+ "@storybook/addon-docs": "9.1.10",
41
+ "@storybook/addon-links": "9.1.10",
42
+ "@storybook/vue3-vite": "9.1.10",
42
43
  "@types/lodash": "4.17.20",
43
44
  "@types/uuid": "11.0.0",
44
45
  "@vitejs/plugin-vue": "6.0.1",
@@ -47,42 +48,42 @@
47
48
  "@vueuse/motion": "^3.0.0",
48
49
  "axios": "1.12.2",
49
50
  "blob-util": "^2.0.2",
50
- "chromatic": "13.2.0",
51
+ "chromatic": "13.3.0",
51
52
  "codemirror": "6.0.2",
52
53
  "cors": "^2.8.5",
53
54
  "cross-env": "^10.0.0",
54
55
  "cy2": "^4.0.0",
55
- "cypress": "15.2.0",
56
+ "cypress": "15.4.0",
56
57
  "express": "^5.0.0",
57
58
  "husky": "9.1.7",
58
59
  "jsdom": "27.0.0",
59
60
  "keycloak-js": "26.1.2",
60
- "lint-staged": "16.1.6",
61
+ "lint-staged": "16.2.4",
61
62
  "lodash": "4.17.21",
62
- "lucide-vue-next": "0.544.0",
63
+ "lucide-vue-next": "0.545.0",
63
64
  "msw-storybook-addon": "^2.0.3",
64
65
  "normalize.css": "8.0.1",
65
66
  "path": "0.12.7",
66
67
  "prettier": "3.6.2",
67
- "sass": "1.93.0",
68
+ "sass": "1.93.2",
68
69
  "semantic-release": "24.2.9",
69
70
  "start-server-and-test": "2.1.2",
70
- "storybook": "9.1.7",
71
- "storybook-addon-pseudo-states": "9.1.7",
71
+ "storybook": "9.1.10",
72
+ "storybook-addon-pseudo-states": "9.1.10",
72
73
  "storybook-vue3-router": "^6.0.2",
73
74
  "typescript": "5.2.2",
74
75
  "uuid": "13.0.0",
75
- "vite": "7.1.6",
76
+ "vite": "7.1.10",
76
77
  "vite-plugin-dts": "^4.1.0",
77
- "vite-plugin-mkcert": "1.17.8",
78
+ "vite-plugin-mkcert": "1.17.9",
78
79
  "vite-plugin-pages": "0.33.1",
79
80
  "vite-svg-loader": "5.1.0",
80
81
  "vitest": "3.2.4",
81
- "vue": "3.5.21",
82
+ "vue": "3.5.22",
82
83
  "vue-codemirror": "6.1.1",
83
84
  "vue-loader": "17.4.2",
84
- "vue-router": "4.5.1",
85
- "vue-tsc": "3.0.7"
85
+ "vue-router": "4.6.0",
86
+ "vue-tsc": "3.1.1"
86
87
  },
87
88
  "files": [
88
89
  "dist",
@@ -82,3 +82,29 @@ export const Tertiary = Template.bind({});
82
82
  Tertiary.args = {
83
83
  type: 'tertiary',
84
84
  };
85
+
86
+ const TemplateSmall = (args) => ({
87
+ components: {
88
+ BmsButton,
89
+ ArrowRight,
90
+ ArrowLeft,
91
+ Camera,
92
+ Heart,
93
+ Wand,
94
+ },
95
+ setup() {
96
+ return { args };
97
+ },
98
+ template: `
99
+ <BmsButton v-bind="args">Save me</BmsButton>
100
+ <br/>
101
+ <br/>
102
+ <BmsButton v-bind="args" small>Save me</BmsButton>
103
+
104
+ `,
105
+ });
106
+
107
+ export const Small = TemplateSmall.bind({});
108
+ Small.args = {
109
+ type: 'primary',
110
+ };
@@ -3,6 +3,7 @@
3
3
  :mode="uiMode"
4
4
  :color="mode"
5
5
  :to="to"
6
+ :small="small"
6
7
  :type="submit ? 'submit' : 'button'"
7
8
  >
8
9
  <template #start>
@@ -26,6 +27,7 @@ interface Props {
26
27
  to?: RouteLocationRaw | null;
27
28
  mode?: StatusType.Danger | StatusType.Default;
28
29
  submit?: boolean;
30
+ small?: boolean;
29
31
  }
30
32
 
31
33
  const props = withDefaults(defineProps<Props>(), {
@@ -38,6 +38,10 @@ const Template = (args) => ({
38
38
  <br> <b>:hover</b>
39
39
  <BmsIconButton v-bind="args" class="_hover"><Heart/></BmsIconButton>
40
40
 
41
+ <br>
42
+ <br> <b>Small</b>
43
+ <BmsIconButton v-bind="args" small><Heart/></BmsIconButton>
44
+
41
45
 
42
46
  <br> <b>BmsIconButton must be on same alignment (using BmsLink and native button html tag)</b>
43
47
  <BmsIconButton
@@ -4,7 +4,14 @@
4
4
  :tooltip-text="tooltipText"
5
5
  :direction="tooltipDirection"
6
6
  >
7
- <UiButton v-bind="$attrs" icon :to="to" :target="target" :color="mode">
7
+ <UiButton
8
+ v-bind="$attrs"
9
+ icon
10
+ :to="to"
11
+ :target="target"
12
+ :color="mode"
13
+ :small="small"
14
+ >
8
15
  <slot></slot>
9
16
  </UiButton>
10
17
  </BmsTooltip>
@@ -23,11 +30,13 @@ interface Props {
23
30
  mode?: StatusType.Danger | StatusType.Information;
24
31
  tooltipText?: string;
25
32
  tooltipDirection?: TooltipDirection;
33
+ small?: boolean;
26
34
  }
27
35
  const props = withDefaults(defineProps<Props>(), {
28
36
  mode: StatusType.Information,
29
37
  to: null,
30
38
  tooltipText: '',
39
+ small: false,
31
40
  });
32
41
 
33
42
  const activated: ComputedRef<boolean> = computed(() => !!props.tooltipText);
@@ -25,6 +25,7 @@ interface Props {
25
25
  color?: StatusType;
26
26
  mode?: 'fill' | 'ghost' | 'outline';
27
27
  icon?: boolean;
28
+ small?: boolean;
28
29
  }
29
30
 
30
31
  const props = withDefaults(defineProps<Props>(), {
@@ -33,5 +34,6 @@ const props = withDefaults(defineProps<Props>(), {
33
34
  color: StatusType.Default,
34
35
  mode: 'fill',
35
36
  icon: false,
37
+ small: false,
36
38
  });
37
39
  </script>
@@ -23,6 +23,7 @@ interface Props {
23
23
  target?: string;
24
24
  color: StatusType;
25
25
  mode: 'fill' | 'ghost' | 'outline';
26
+ small: boolean;
26
27
  icon: boolean;
27
28
  }
28
29
 
@@ -33,7 +34,7 @@ const classes = computed(() => {
33
34
  const disabled = attrs && (!!attrs.disabled || attrs.disabled === '');
34
35
  return `bms-button is-${props.color}${
35
36
  props.icon ? ' is-icon' : ` bms-button--${props.mode}`
36
- } ${disabled ? ' bms-button--disabled' : ''} `;
37
+ } ${disabled ? ' bms-button--disabled' : ''} ${props.small ? 'bms-button--small' : ''}`;
37
38
  });
38
39
  </script>
39
40
 
@@ -44,8 +45,6 @@ const classes = computed(() => {
44
45
 
45
46
  @mixin selectColorsToApply($colorName) {
46
47
  &.is-icon {
47
- padding: 0.5em 0;
48
-
49
48
  --bms-button-background-color: transparent;
50
49
  --bms-button-border-color: transparent;
51
50
  --bms-button-text-color: var(--bms-grey-100);
@@ -127,14 +126,15 @@ const classes = computed(() => {
127
126
  }
128
127
 
129
128
  .bms-button {
130
- height: 2em;
131
129
  font-size: 1em;
132
130
 
131
+ --button-padding: 0.5em;
132
+
133
133
  box-sizing: border-box;
134
134
  display: inline-flex;
135
135
  align-items: center;
136
136
  margin: 0;
137
- padding: 0.5em;
137
+ padding: var(--button-padding);
138
138
  appearance: none;
139
139
  cursor: pointer;
140
140
  border-radius: var(--bms-border-radius);
@@ -152,7 +152,7 @@ const classes = computed(() => {
152
152
  @include selectColorsToApply('grey');
153
153
 
154
154
  .content {
155
- margin: 0 0.5em;
155
+ margin: 0 var(--button-padding);
156
156
  }
157
157
 
158
158
  > span {
@@ -166,6 +166,13 @@ const classes = computed(() => {
166
166
  width: 1em;
167
167
  height: 1em;
168
168
  }
169
+ &--small {
170
+ --button-padding: 0.25em;
171
+ }
172
+
173
+ &.is-icon {
174
+ padding: var(--button-padding) 0;
175
+ }
169
176
 
170
177
  &.is-default {
171
178
  @include selectColorsToApply('main');
@@ -134,6 +134,7 @@ const style = computed(() => {
134
134
  filter: drop-shadow(0 0 0.8rem var(--bms-shadow-color));
135
135
  color: var(--bms-font-color);
136
136
  transform: var(--bms-tooltip-transform);
137
+ overflow-wrap: break-word;
137
138
 
138
139
  &::before {
139
140
  content: '';
@@ -40,6 +40,18 @@ Default.args = {
40
40
  modelValue: '',
41
41
  };
42
42
 
43
+ export const Loading = Template.bind({});
44
+ Loading.args = {
45
+ label: 'My autocomplete field',
46
+ loading: true,
47
+ options: [
48
+ { label: 'titi', value: 'i' },
49
+ { label: 'toto', value: 'o' },
50
+ { label: 'tutu', value: 'u' },
51
+ ],
52
+ modelValue: '',
53
+ };
54
+
43
55
  export const WithStringArray = Template.bind({});
44
56
  WithStringArray.args = {
45
57
  label: 'My autocomplete field',
@@ -13,6 +13,7 @@
13
13
  :placeholder="placeholder"
14
14
  :can-add-new-option="canAddNewOption"
15
15
  :small="small"
16
+ :loading="loading"
16
17
  @select="(option: any) => emits('select', option)"
17
18
  @add-new-option="(newOption: string) => emits('addNewOption', newOption)"
18
19
  @input="(e: InputEvent) => emits('input', e)"
@@ -45,6 +45,14 @@ Default.args = {
45
45
  captions: ['There is more information'],
46
46
  };
47
47
 
48
+ export const Loading = Template.bind({});
49
+ Loading.args = {
50
+ label: 'My label',
51
+ placeholder: 'Placeholder',
52
+ captions: ['There is more information'],
53
+ loading: true,
54
+ };
55
+
48
56
  export const WithValue = Template.bind({});
49
57
  WithValue.args = {
50
58
  label: 'My label',
@@ -38,3 +38,30 @@ On.args = { label: 'Label', modelValue: true, name: 'my-input' };
38
38
 
39
39
  export const Off = Template.bind({});
40
40
  Off.args = { label: 'Label', modelValue: false, name: 'my-input' };
41
+
42
+ const TemplateOverflow = (args) => ({
43
+ setup() {
44
+ const curentValue = ref(args.modelValue);
45
+ return { args, curentValue };
46
+ },
47
+ components: {
48
+ BmsInputToggle,
49
+ },
50
+ template: `
51
+ <div style="width: 200px; border: 1px solid black;">
52
+ <BmsInputToggle v-bind="{...args}" v-model="curentValue"/>
53
+ </div>
54
+ `,
55
+ });
56
+ export const Overflow = TemplateOverflow.bind({});
57
+ Overflow.args = {
58
+ label: 'with a very long label that overflows',
59
+ name: 'my-input',
60
+ ellipsisLabel: true,
61
+ };
62
+ export const NoOverflow = TemplateOverflow.bind({});
63
+ NoOverflow.args = {
64
+ label: 'with a very long label that overflows',
65
+ name: 'my-input',
66
+ ellipsisLabel: false,
67
+ };
@@ -1,6 +1,9 @@
1
1
  <template>
2
- <field :disabled="disabled" :small="small">
3
- <label class="input-toggle" :class="{ isOn: modelValue, disabled }">
2
+ <field :disabled="disabled" :small="small" :ellipsisLabel="ellipsisLabel">
3
+ <label
4
+ class="input-toggle"
5
+ :class="{ isOn: modelValue, disabled, overflow: ellipsisLabel }"
6
+ >
4
7
  <span class="input-switch-info">{{ label }}</span>
5
8
  <UiBmsSwitch
6
9
  :modelValue="modelValue"
@@ -43,6 +46,17 @@ defineEmits<{
43
46
  align-items: center;
44
47
  gap: 0.5em;
45
48
 
49
+ &.overflow {
50
+ width: 100%;
51
+ .input-switch-info {
52
+ width: 100%;
53
+ white-space: nowrap;
54
+ overflow: hidden;
55
+ word-break: break-all;
56
+ text-overflow: ellipsis;
57
+ }
58
+ }
59
+
46
60
  .input-switch-info {
47
61
  color: var(--label-color);
48
62
  transition: 0.2s;
@@ -23,6 +23,7 @@
23
23
  ref="inputElement"
24
24
  v-model="searching"
25
25
  class="search"
26
+ :disabled="disabled"
26
27
  @focus="openDatalist"
27
28
  @click="openDatalist"
28
29
  @keyup.down="openDatalist"
@@ -84,7 +85,11 @@ const inputElement: Ref<HTMLElement | null> = ref(null);
84
85
  const isDatalistOpen = ref(false);
85
86
 
86
87
  const closeDatalist = () => (isDatalistOpen.value = false);
87
- const openDatalist = () => (isDatalistOpen.value = true);
88
+ const openDatalist = () => {
89
+ if (!props.disabled) {
90
+ isDatalistOpen.value = true;
91
+ }
92
+ };
88
93
 
89
94
  const searching = ref('');
90
95
 
@@ -39,6 +39,15 @@ Default.args = {
39
39
  ],
40
40
  };
41
41
 
42
+ export const Loading = Template.bind({});
43
+ Loading.args = {
44
+ label: 'My label',
45
+ captions: ['Helping text'],
46
+ loading: true,
47
+ modelValue: '',
48
+ options: [],
49
+ };
50
+
42
51
  export const WithPlaceholder = Template.bind({});
43
52
  WithPlaceholder.args = {
44
53
  label: 'My label',
@@ -64,8 +64,10 @@ const closeDatalist = () => {
64
64
  isDatalistOpen.value = false;
65
65
  };
66
66
  const openDatalist = () => {
67
- isDatalistOpen.value = true;
68
- setFocus();
67
+ if (!props.disabled) {
68
+ isDatalistOpen.value = true;
69
+ setFocus();
70
+ }
69
71
  };
70
72
 
71
73
  const emits = defineEmits<{
@@ -34,8 +34,8 @@ const Template = (args) => ({
34
34
  return { args };
35
35
  },
36
36
  template: `
37
- <BmsServerAutocomplete v-bind="args">
38
- </BmsServerAutocomplete>
37
+ <BmsServerAutocomplete v-bind="args"/>
38
+ <BmsServerAutocomplete v-bind="args" small/>
39
39
  `,
40
40
  });
41
41
 
@@ -51,6 +51,29 @@ Default.args = {
51
51
  url: 'https://fakeapi.com/items',
52
52
  };
53
53
 
54
+ export const Disabled = Template.bind({});
55
+ Disabled.args = {
56
+ label: 'My autocomplete field',
57
+ modelValue: '',
58
+ disabled: true,
59
+ request: Promise.resolve([]),
60
+ };
61
+
62
+ export const Loading = Template.bind({});
63
+ Loading.args = {
64
+ label: 'My autocomplete field',
65
+ modelValue: '',
66
+ request: () => {
67
+ return new Promise((resolve) =>
68
+ setTimeout(() => {
69
+ resolve({
70
+ data: [],
71
+ });
72
+ }, 10000),
73
+ );
74
+ },
75
+ };
76
+
54
77
  export const WithRequest = Template.bind({});
55
78
  WithRequest.args = {
56
79
  label: 'My autocomplete field',
@@ -13,6 +13,7 @@
13
13
  :placeholder="placeholder"
14
14
  :small="small"
15
15
  :canAddNewOption="false"
16
+ :loading="loading"
16
17
  @input="onInput"
17
18
  @select="(option: any) => emits('select', option)"
18
19
  >
@@ -7,6 +7,7 @@
7
7
  :class="classes"
8
8
  :required="required"
9
9
  :placeholder="placeholder"
10
+ :disabled="disabled"
10
11
  @input="onInput"
11
12
  @keydown.enter.stop
12
13
  cols="10"
@@ -93,7 +94,6 @@ defineExpose({
93
94
  &.is-disabled {
94
95
  --field-border-color: var(--bms-grey-25);
95
96
  --textarea-background-color: var(--bms-grey-25);
96
- pointer-events: none;
97
97
  }
98
98
  &.is-small {
99
99
  min-height: 56px;
@@ -56,6 +56,7 @@ input {
56
56
  border-radius: var(--bms-border-radius-xlarge);
57
57
  position: relative;
58
58
  transition: 0.2s;
59
+ flex-shrink: 0;
59
60
 
60
61
  &::before {
61
62
  content: '';
@@ -92,7 +92,7 @@ WithScroll.args = {
92
92
  export const Success = Template.bind({});
93
93
  Success.args = {
94
94
  modelValue: true,
95
- title: 'Titre',
95
+ title: 'Titre de ma modal',
96
96
  type: StatusType.Success,
97
97
  };
98
98
 
@@ -246,7 +246,6 @@ async function fetchData() {
246
246
  }
247
247
  controller.value = new AbortController();
248
248
  loading.value = true;
249
-
250
249
  try {
251
250
  const { data, total: totalItems } = await props.request(
252
251
  {
@@ -260,11 +259,8 @@ async function fetchData() {
260
259
  controller.value,
261
260
  props.url,
262
261
  );
263
-
264
262
  items.value = data;
265
263
  total.value = totalItems;
266
-
267
- loading.value = false;
268
264
  } catch (error) {
269
265
  if (
270
266
  !(error instanceof CanceledError) &&
@@ -272,6 +268,8 @@ async function fetchData() {
272
268
  ) {
273
269
  throw error;
274
270
  }
271
+ } finally {
272
+ loading.value = false;
275
273
  }
276
274
  }
277
275
 
@@ -1,10 +1,11 @@
1
1
  import FieldComponent from '@/plugins/field/FieldComponent.vue';
2
2
  import { mount, shallowMount } from '@vue/test-utils';
3
3
  import { BmsCaption } from '@/index';
4
+ import { nextTick } from 'vue';
4
5
 
5
6
  describe('FieldComponent', () => {
6
7
  it('should display label', async () => {
7
- const wrapper = shallowMount(FieldComponent, {
8
+ const wrapper = mount(FieldComponent, {
8
9
  slots: {
9
10
  default: '<input type="text" />',
10
11
  },
@@ -17,6 +17,7 @@ const Template = (args) => ({
17
17
  Album,
18
18
  },
19
19
  template: `
20
+ <div style="border:1px solid grey; width: 400px;">
20
21
  <field v-bind="args">
21
22
  <template #icon-start v-if="args.iconStart">
22
23
  <Flashlight/>
@@ -26,12 +27,28 @@ const Template = (args) => ({
26
27
  <Album/>
27
28
  </template>
28
29
  </field>
29
- `,
30
+ </div>
31
+ `,
30
32
  });
31
33
 
32
34
  export const Default = Template.bind({});
33
35
  Default.args = {
34
36
  label: 'My label',
37
+ ellipsisLabel: true,
38
+ captions: ['Helping text'],
39
+ };
40
+
41
+ export const NoOverflow = Template.bind({});
42
+ NoOverflow.args = {
43
+ label: 'My label looooooooooooooooooooooooooooooooooooooooooooooong',
44
+ ellipsisLabel: false,
45
+ captions: ['Helping text'],
46
+ };
47
+
48
+ export const Overflow = Template.bind({});
49
+ Overflow.args = {
50
+ label: 'My label looooooooooooooooooooooooooooooooooooooooooooooong',
51
+ ellipsisLabel: true,
35
52
  captions: ['Helping text'],
36
53
  };
37
54
 
@@ -1,8 +1,15 @@
1
1
  <template>
2
- <div :class="classes">
2
+ <div :class="classes" ref="parentElement">
3
3
  <div class="field__wrapper">
4
4
  <span class="field__label">
5
- <span class="field__label__title">{{ label }}</span>
5
+ <span
6
+ class="field__label__title"
7
+ :class="{ ellipsis: ellipsisLabel }"
8
+ :title="ellipsisLabel ? label : undefined"
9
+ >
10
+ {{ label }}
11
+ </span>
12
+
6
13
  <span v-if="required" class="field__label-required">*</span>
7
14
  <span v-if="optional && !required" class="field__label-optional">
8
15
  (Optionnel)
@@ -15,7 +22,8 @@
15
22
  </span>
16
23
 
17
24
  <span class="field__input">
18
- <slot></slot>
25
+ <SkeletonField v-if="loading" />
26
+ <slot v-else></slot>
19
27
  </span>
20
28
  <span ref="datalist">
21
29
  <span class="field__datalist" :class="{ top: openTop }">
@@ -53,16 +61,20 @@ import { useElementBounding, useWindowSize } from '@vueuse/core';
53
61
  import { Caption } from '@/models/caption.model';
54
62
  import BmsCaption from '@/components/feedback/BmsCaption.vue';
55
63
  import { FieldComponentProps } from './field-component.model';
64
+ import SkeletonField from './SkeletonField.vue';
56
65
 
57
66
  const props = withDefaults(defineProps<FieldComponentProps>(), {
58
67
  label: '',
59
68
  optional: false,
60
69
  small: false,
70
+ loading: false,
71
+ ellipsisLabel: false,
61
72
  });
62
73
 
63
74
  const datalist: Ref<HTMLSpanElement | null> = ref<HTMLSpanElement | null>(null);
64
- const { y } = useElementBounding(datalist);
65
75
 
76
+ const { y } = useElementBounding(datalist);
77
+ const parentElement = ref<HTMLElement | null>(null);
66
78
  const { height: windowHeight } = useWindowSize();
67
79
 
68
80
  const classes = computed(() => {
@@ -150,9 +162,17 @@ const getCaptionIdentifier = (caption: string | Caption): string => {
150
162
  align-items: center;
151
163
 
152
164
  &__title {
165
+ display: inline-block;
166
+ white-space: nowrap;
167
+
153
168
  font-size: 1em;
154
169
  font-weight: var(--field-label-font-weight);
155
170
  color: var(--field-label-color);
171
+
172
+ &.ellipsis {
173
+ overflow: hidden;
174
+ text-overflow: ellipsis;
175
+ }
156
176
  }
157
177
 
158
178
  &-helper--icon {