design-system-next 2.12.3 → 2.12.7

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.
@@ -1,7 +1,15 @@
1
1
  import { ref, onMounted } from 'vue';
2
2
 
3
3
  import type { SetupContext } from 'vue';
4
- import type { SidenavPropTypes, SidenavEmitTypes, ParentLinkItem, NavLinks, NavItem, MenuLinkItem, Attributes } from './sidenav';
4
+ import type {
5
+ SidenavPropTypes,
6
+ SidenavEmitTypes,
7
+ ParentLinkItem,
8
+ NavLinks,
9
+ NavItem,
10
+ MenuLinkItem,
11
+ Attributes,
12
+ } from './sidenav';
5
13
 
6
14
  interface ObjectItem {
7
15
  redirect?: {
@@ -23,16 +31,6 @@ export const useSidenav = (props: SidenavPropTypes, emit: SetupContext<SidenavEm
23
31
  const isQuckActionMenuVisible = ref(false);
24
32
  const isUserMenuVisible = ref(false);
25
33
 
26
- const userProfileError = ref(false);
27
-
28
- const getUserInitials = (name: string) => {
29
- const nameArray = name.split(' ');
30
-
31
- const initials = nameArray[0].charAt(0) + (nameArray[1] ? nameArray[1].charAt(0) : '');
32
-
33
- return initials.toUpperCase();
34
- };
35
-
36
34
  const handleRedirect = (objectItem: ObjectItem, parentNav: string, menu: string, submenu: string) => {
37
35
  if (objectItem && objectItem.redirect) {
38
36
  if (objectItem.redirect.openInNewTab) {
@@ -92,7 +90,7 @@ export const useSidenav = (props: SidenavPropTypes, emit: SetupContext<SidenavEm
92
90
  }
93
91
  };
94
92
 
95
- const groupByGroupId = (items: NavItem[]) => {
93
+ const groupByGroupId = (items: NavItem[]): { parentLinks: ParentLinkItem[] }[] => {
96
94
  const groups: Record<string, NavItem[]> = {};
97
95
 
98
96
  items.forEach((item) => {
@@ -102,7 +100,9 @@ export const useSidenav = (props: SidenavPropTypes, emit: SetupContext<SidenavEm
102
100
  groups[item.groupId].push(item);
103
101
  });
104
102
 
105
- return Object.values(groups).map((group) => ({ parentLinks: group.map(mapItemToNav) }));
103
+ return Object.values(groups).map((group) => ({
104
+ parentLinks: group.map((item) => mapItemToNav(item) as ParentLinkItem),
105
+ }));
106
106
  };
107
107
 
108
108
  const mapItemToNav = (item: NavItem): ParentLinkItem | MenuLinkItem => {
@@ -133,12 +133,12 @@ export const useSidenav = (props: SidenavPropTypes, emit: SetupContext<SidenavEm
133
133
 
134
134
  return {
135
135
  title: item.label,
136
- icon: item.icon || undefined,
136
+ icon: item.icon ?? '',
137
137
  redirect: item.url
138
138
  ? {
139
139
  openInNewTab: item.isNewTab || false,
140
140
  isAbsoluteURL: !confirmIfOwnDomain(item.url as string),
141
- link: navLinkCondition(item),
141
+ link: navLinkCondition(item) ?? '',
142
142
  }
143
143
  : undefined,
144
144
  menuLinks: mapGroupedChildren(groupedChildren, 'menuHeading') as {
@@ -182,13 +182,23 @@ export const useSidenav = (props: SidenavPropTypes, emit: SetupContext<SidenavEm
182
182
  return transformedData;
183
183
  };
184
184
 
185
- const getLozengeTone = (attr: Attributes) => {
186
- if (typeof attr === 'object' && attr !== null && 'tone' in attr && typeof attr.tone === 'string' && ['danger', 'information', 'plain', 'pending', 'success', 'neutral', 'caution'].includes(attr.tone)) {
185
+ const getLozengeTone = (attr: Attributes) => {
186
+ if (
187
+ typeof attr === 'object' &&
188
+ attr !== null &&
189
+ 'tone' in attr &&
190
+ typeof attr.tone === 'string' &&
191
+ ['danger', 'information', 'plain', 'pending', 'success', 'neutral', 'caution'].includes(attr.tone)
192
+ ) {
187
193
  return attr.tone as 'danger' | 'information' | 'plain' | 'pending' | 'success' | 'neutral' | 'caution';
188
194
  }
189
195
  return 'success'; // Default tone
190
196
  };
191
197
 
198
+ const getLozengeLabel = (attr: Attributes) => {
199
+ return attr.value && typeof attr?.value === 'object' && 'label' in attr.value ? String(attr.value.label) : '';
200
+ };
201
+
192
202
  onMounted(async () => {
193
203
  if (props.isNavApi) {
194
204
  navLinks.value = await transformedNavItems(props.navLinks);
@@ -202,11 +212,10 @@ export const useSidenav = (props: SidenavPropTypes, emit: SetupContext<SidenavEm
202
212
  navLinks,
203
213
  isQuckActionMenuVisible,
204
214
  isUserMenuVisible,
205
- userProfileError,
206
- getUserInitials,
207
215
  handleRedirect,
208
216
  generateId,
209
217
  transformedNavItems,
210
- getLozengeTone
218
+ getLozengeTone,
219
+ getLozengeLabel,
211
220
  };
212
221
  };
@@ -21,7 +21,7 @@
21
21
  <table aria-describedby="describe" class="spr-h-full spr-w-full spr-table-fixed" cellspacing="0" cellpadding="0">
22
22
  <thead>
23
23
  <tr v-if="!(props.removeHeaderOnEmpty && sortedData.length <= 0)">
24
- <th v-if="props.isMultiSelect" :class="[getTableClasses.multiselectClass, getTableClasses.headerClasses]">
24
+ <th v-if="props.isMultiSelect" :class="[getTableClasses.multiselectClass, getTableClasses.headerClasses(null)]">
25
25
  <div class="spr-flex spr-items-center spr-justify-center">
26
26
  <spr-checkbox
27
27
  label=""
@@ -96,8 +96,8 @@ export const useTable = (props: TablePropTypes, emit: SetupContext<TableEmitType
96
96
  'spr-background-color': props.variant === 'white',
97
97
  'spr-background-color-surface': props.variant === 'surface',
98
98
  });
99
- const headerClasses = (header: Header) => {
100
- if (header.customTailwindClasses){
99
+ const headerClasses = (header: Header | null) => {
100
+ if (header?.customTailwindClasses){
101
101
  return classNames(header.customTailwindClasses);
102
102
  }
103
103
 
@@ -1,112 +0,0 @@
1
- import { mount } from '@vue/test-utils';
2
- import { describe, it, expect } from 'vitest';
3
- import DatePicker from '../date-picker.vue';
4
-
5
- describe('DatePicker', () => {
6
- it('should render the component', () => {
7
- const wrapper = mount(DatePicker, {
8
- props: {
9
- id: 'test-date-picker',
10
- modelValue: '',
11
- },
12
- });
13
-
14
- expect(wrapper.exists()).toBe(true);
15
- });
16
-
17
- it('should use default date format (MM-DD-YYYY) when no format prop is provided', async () => {
18
- const wrapper = mount(DatePicker, {
19
- props: {
20
- id: 'test-date-picker',
21
- modelValue: '',
22
- },
23
- });
24
-
25
- // Simulate user selecting a date
26
- const dateToSelect = '2023-05-15'; // ISO format
27
-
28
- // Find the input element and simulate user input
29
- const input = wrapper.find('input');
30
- await input.setValue(dateToSelect);
31
- await input.trigger('change');
32
-
33
- // Check that the emitted value uses the default format MM-DD-YYYY
34
- const emittedValue = wrapper.emitted('update:modelValue');
35
- expect(emittedValue).toBeDefined();
36
- expect(emittedValue![emittedValue!.length - 1][0]).toMatch(/\d{2}-\d{2}-\d{4}/); // MM-DD-YYYY format
37
- });
38
-
39
- it('should format the date according to the specified format prop (YYYY-MM-DD)', async () => {
40
- const wrapper = mount(DatePicker, {
41
- props: {
42
- id: 'test-date-picker',
43
- modelValue: '',
44
- format: 'YYYY-MM-DD',
45
- },
46
- });
47
-
48
- // Simulate user selecting a date
49
- const dateToSelect = '05/15/2023'; // Different format input
50
-
51
- // Find the input element and simulate user input
52
- const input = wrapper.find('input');
53
- await input.setValue(dateToSelect);
54
- await input.trigger('change');
55
-
56
- // Check that the emitted value uses the specified format YYYY-MM-DD
57
- const emittedValue = wrapper.emitted('update:modelValue');
58
- expect(emittedValue).toBeDefined();
59
- expect(emittedValue![emittedValue!.length - 1][0]).toMatch(/\d{4}-\d{2}-\d{2}/); // YYYY-MM-DD format
60
- });
61
-
62
- it('should format the date according to the specified format prop (MM/DD/YYYY)', async () => {
63
- const wrapper = mount(DatePicker, {
64
- props: {
65
- id: 'test-date-picker',
66
- modelValue: '',
67
- format: 'MM/DD/YYYY',
68
- },
69
- });
70
-
71
- // Simulate user selecting a date
72
- const dateToSelect = '2023-05-15'; // ISO format
73
-
74
- // Find the input element and simulate user input
75
- const input = wrapper.find('input');
76
- await input.setValue(dateToSelect);
77
- await input.trigger('change');
78
-
79
- // Check that the emitted value uses the specified format MM/DD/YYYY
80
- const emittedValue = wrapper.emitted('update:modelValue');
81
- expect(emittedValue).toBeDefined();
82
- expect(emittedValue![emittedValue!.length - 1][0]).toMatch(/\d{2}\/\d{2}\/\d{4}/); // MM/DD/YYYY format
83
- });
84
-
85
- it('should parse dates correctly with different format (MM/DD/YYYY)', async () => {
86
- const wrapper = mount(DatePicker, {
87
- props: {
88
- id: 'test-date-picker',
89
- modelValue: '05/15/2023', // Using MM/DD/YYYY format
90
- format: 'MM/DD/YYYY',
91
- },
92
- });
93
-
94
- // Check that the component correctly displays the date in the input
95
- const input = wrapper.find('input');
96
- expect(input.element.value).toBe('05/15/2023');
97
- });
98
-
99
- it('should parse dates correctly with different format (YYYY-MM-DD)', async () => {
100
- const wrapper = mount(DatePicker, {
101
- props: {
102
- id: 'test-date-picker',
103
- modelValue: '2023-05-15', // Using YYYY-MM-DD format
104
- format: 'YYYY-MM-DD',
105
- },
106
- });
107
-
108
- // Check that the component correctly displays the date in the input
109
- const input = wrapper.find('input');
110
- expect(input.element.value).toBe('2023-05-15');
111
- });
112
- });
@@ -1,106 +0,0 @@
1
- // Test script for dropdown component
2
-
3
- import { describe, it, expect } from 'vitest';
4
- import { ref } from 'vue';
5
- import { useDropdown } from '../use-dropdown';
6
-
7
- describe('Dropdown Component Fixes', () => {
8
- describe('Value handling', () => {
9
- it('should handle string value selection correctly', () => {
10
- // Setup single-select dropdown with string value
11
- const modelValue = ref('');
12
- const menuList = [
13
- { text: 'Apple', value: 'apple' },
14
- { text: 'Banana', value: 'banana' }
15
- ];
16
-
17
- const { handleSelectedItem } = useDropdown({
18
- id: 'test',
19
- modelValue,
20
- menuList
21
- });
22
-
23
- // Simulate selecting an item
24
- handleSelectedItem([{ text: 'Apple', value: 'apple' }]);
25
-
26
- // Verify correct value is set
27
- expect(modelValue.value).toBe('apple');
28
- });
29
-
30
- it('should handle multiple selection correctly', () => {
31
- // Setup multi-select dropdown
32
- const modelValue = ref([]);
33
- const menuList = [
34
- { text: 'Apple', value: 'apple' },
35
- { text: 'Banana', value: 'banana' }
36
- ];
37
-
38
- const { handleSelectedItem } = useDropdown({
39
- id: 'test',
40
- modelValue,
41
- menuList,
42
- multiSelect: true
43
- });
44
-
45
- // Simulate selecting multiple items
46
- handleSelectedItem([
47
- { text: 'Apple', value: 'apple' },
48
- { text: 'Banana', value: 'banana' }
49
- ]);
50
-
51
- // Verify correct values are set
52
- expect(modelValue.value).toEqual(['apple', 'banana']);
53
- });
54
- });
55
-
56
- describe('Ladderized dropdown', () => {
57
- it('should handle ladderized dropdown selection', () => {
58
- // Setup ladderized dropdown
59
- const modelValue = ref([]);
60
- const menuList = [
61
- {
62
- text: 'Lion',
63
- value: 'lion',
64
- sublevel: [
65
- { text: 'Cub', value: 'cub' }
66
- ]
67
- }
68
- ];
69
-
70
- const { handleSelectedItem } = useDropdown({
71
- id: 'test',
72
- modelValue,
73
- menuList,
74
- ladderized: true
75
- });
76
-
77
- // Simulate selecting an item
78
- handleSelectedItem([{ text: 'Cub', value: 'cub', subvalue: 'lion' }]);
79
-
80
- // Verify correct values are set for ladderized dropdown
81
- expect(modelValue.value).toEqual(['lion', 'cub']);
82
- });
83
- });
84
-
85
- describe('Edge cases', () => {
86
- it('should handle empty selection', () => {
87
- const modelValue = ref('apple');
88
- const menuList = [
89
- { text: 'Apple', value: 'apple' },
90
- { text: 'Banana', value: 'banana' }
91
- ];
92
-
93
- const { handleSelectedItem } = useDropdown({
94
- id: 'test',
95
- modelValue,
96
- menuList
97
- });
98
-
99
- // Simulate clearing selection
100
- handleSelectedItem([]);
101
-
102
- // Verify value is cleared
103
- expect(modelValue.value).toBeUndefined();
104
- });
105
- });
106
- });
@@ -1,213 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { ref } from 'vue';
3
- import { useDropdown } from '../use-dropdown';
4
-
5
- describe('Dropdown Value Types', () => {
6
- describe('Single primitive values', () => {
7
- it('should handle string values', () => {
8
- const modelValue = 'apple';
9
- const menuList = ['apple', 'banana', 'cherry'];
10
-
11
- const { dropdownMenuList, normalizedValue } = useDropdown({
12
- id: 'test',
13
- modelValue,
14
- menuList
15
- });
16
-
17
- expect(normalizedValue.value).toEqual(['apple']);
18
- expect(dropdownMenuList.value).toEqual([
19
- { text: 'apple', value: 'apple' },
20
- { text: 'banana', value: 'banana' },
21
- { text: 'cherry', value: 'cherry' }
22
- ]);
23
- });
24
-
25
- it('should handle number values', () => {
26
- const modelValue = 42;
27
- const menuList = [42, 55, 67];
28
-
29
- const { dropdownMenuList, normalizedValue } = useDropdown({
30
- id: 'test',
31
- modelValue,
32
- menuList
33
- });
34
-
35
- expect(normalizedValue.value).toEqual([42]);
36
- expect(dropdownMenuList.value).toEqual([
37
- { text: '42', value: '42' },
38
- { text: '55', value: '55' },
39
- { text: '67', value: '67' }
40
- ]);
41
- });
42
- });
43
-
44
- describe('Single object values', () => {
45
- it('should handle object values', () => {
46
- const employee = { systemId: 1, firstName: 'John', lastName: 'Doe' };
47
- const modelValue = employee;
48
- const menuList = [
49
- { systemId: 1, firstName: 'John', lastName: 'Doe' },
50
- { systemId: 2, firstName: 'Jane', lastName: 'Smith' }
51
- ];
52
-
53
- const { dropdownMenuList, normalizedValue } = useDropdown({
54
- id: 'test',
55
- modelValue,
56
- menuList,
57
- textField: 'firstName',
58
- valueField: 'systemId'
59
- });
60
-
61
- expect(normalizedValue.value).toEqual([employee]);
62
- expect(dropdownMenuList.value[0].text).toBe('John');
63
- expect(dropdownMenuList.value[1].text).toBe('Jane');
64
- expect(dropdownMenuList.value[0].value).toBe('1');
65
- expect(dropdownMenuList.value[0]._originalObject).toEqual(menuList[0]);
66
- });
67
- });
68
-
69
- describe('Multiple primitive values', () => {
70
- it('should handle string arrays', () => {
71
- const modelValue = ['apple', 'banana'];
72
- const menuList = ['apple', 'banana', 'cherry'];
73
-
74
- const { dropdownMenuList, normalizedValue } = useDropdown({
75
- id: 'test',
76
- modelValue,
77
- menuList,
78
- multiSelect: true
79
- });
80
-
81
- expect(normalizedValue.value).toEqual(['apple', 'banana']);
82
- expect(dropdownMenuList.value).toEqual([
83
- { text: 'apple', value: 'apple' },
84
- { text: 'banana', value: 'banana' },
85
- { text: 'cherry', value: 'cherry' }
86
- ]);
87
- });
88
-
89
- it('should handle number arrays', () => {
90
- const modelValue = [42, 55];
91
- const menuList = [42, 55, 67];
92
-
93
- const { dropdownMenuList, normalizedValue } = useDropdown({
94
- id: 'test',
95
- modelValue,
96
- menuList,
97
- multiSelect: true
98
- });
99
-
100
- expect(normalizedValue.value).toEqual([42, 55]);
101
- expect(dropdownMenuList.value).toEqual([
102
- { text: '42', value: '42' },
103
- { text: '55', value: '55' },
104
- { text: '67', value: '67' }
105
- ]);
106
- });
107
- });
108
-
109
- describe('Multiple object values', () => {
110
- it('should handle object arrays', () => {
111
- const employees = [
112
- { systemId: 1, firstName: 'John', lastName: 'Doe' },
113
- { systemId: 2, firstName: 'Jane', lastName: 'Smith' }
114
- ];
115
- const modelValue = employees;
116
- const menuList = [
117
- { systemId: 1, firstName: 'John', lastName: 'Doe' },
118
- { systemId: 2, firstName: 'Jane', lastName: 'Smith' },
119
- { systemId: 3, firstName: 'Mike', lastName: 'Johnson' }
120
- ];
121
-
122
- const { dropdownMenuList, normalizedValue } = useDropdown({
123
- id: 'test',
124
- modelValue,
125
- menuList,
126
- textField: 'firstName',
127
- valueField: 'systemId',
128
- multiSelect: true
129
- });
130
-
131
- expect(normalizedValue.value).toEqual(employees);
132
- expect(dropdownMenuList.value[0].text).toBe('John');
133
- expect(dropdownMenuList.value[1].text).toBe('Jane');
134
- expect(dropdownMenuList.value[0].value).toBe('1');
135
- expect(dropdownMenuList.value[0]._originalObject).toEqual(menuList[0]);
136
- });
137
- });
138
-
139
- describe('Handle selected items', () => {
140
- it('should output string for string selection', () => {
141
- const modelValue = ref(undefined);
142
- const menuList = ['apple', 'banana', 'cherry'];
143
-
144
- const { handleSelectedItem } = useDropdown({
145
- id: 'test',
146
- modelValue,
147
- menuList,
148
- });
149
-
150
- handleSelectedItem([{ text: 'apple', value: 'apple' }]);
151
- expect(modelValue.value).toBe('apple');
152
- });
153
-
154
- it('should output number for number selection', () => {
155
- const modelValue = ref(undefined);
156
- const menuList = [42, 55, 67];
157
-
158
- const { handleSelectedItem } = useDropdown({
159
- id: 'test',
160
- modelValue,
161
- menuList,
162
- });
163
-
164
- handleSelectedItem([{ text: '42', value: '42' }]);
165
- expect(modelValue.value).toBe(42);
166
- });
167
-
168
- it('should output original object for object selection', () => {
169
- const modelValue = ref(undefined);
170
- const employeeObj = { systemId: 1, firstName: 'John', lastName: 'Doe' };
171
- const menuList = [
172
- employeeObj,
173
- { systemId: 2, firstName: 'Jane', lastName: 'Smith' }
174
- ];
175
-
176
- const { handleSelectedItem, dropdownMenuList } = useDropdown({
177
- id: 'test',
178
- modelValue,
179
- menuList,
180
- textField: 'firstName',
181
- valueField: 'systemId'
182
- });
183
-
184
- // Process menu list first to get _originalObject
185
- dropdownMenuList.value.forEach(item => {
186
- if (item.value === '1') {
187
- handleSelectedItem([item]);
188
- }
189
- });
190
-
191
- expect(modelValue.value).toEqual(employeeObj);
192
- });
193
-
194
- it('should output array for multi-select', () => {
195
- const modelValue = ref([]);
196
- const menuList = ['apple', 'banana', 'cherry'];
197
-
198
- const { handleSelectedItem } = useDropdown({
199
- id: 'test',
200
- modelValue,
201
- menuList,
202
- multiSelect: true
203
- });
204
-
205
- handleSelectedItem([
206
- { text: 'apple', value: 'apple' },
207
- { text: 'banana', value: 'banana' }
208
- ]);
209
-
210
- expect(modelValue.value).toEqual(['apple', 'banana']);
211
- });
212
- });
213
- });
@@ -1,76 +0,0 @@
1
- // Example usage of dropdown with primitive number values in multi-select mode
2
-
3
- <template>
4
- <div>
5
- <h2>Multiple Number Values Demo</h2>
6
- <p>Selected values: {{ displaySelection }}</p>
7
-
8
- <div class="dropdown-container">
9
- <spr-dropdown
10
- id="number-multi-dropdown"
11
- v-model="selectedNumbers"
12
- :menu-list="numberOptions"
13
- multi-select
14
- @update:model-value="handleSelectedItems"
15
- >
16
- <spr-input
17
- v-model="displayText"
18
- label="Select Numbers"
19
- readonly
20
- placeholder="Select numbers..."
21
- />
22
- </spr-dropdown>
23
- </div>
24
-
25
- <div class="mt-4">
26
- <h3>Current selection type:</h3>
27
- <pre>{{ typeof selectedNumbers[0] }}</pre>
28
-
29
- <h3>Current selection:</h3>
30
- {{ selectedNumbers }}
31
- </div>
32
- </div>
33
- </template>
34
-
35
- <script setup>
36
- import { ref, computed } from 'vue';
37
- import SprInput from "@/components/input/input.vue";
38
- import SprDropdown from "@/components/dropdown/dropdown.vue";
39
-
40
- // Define number options - raw number values
41
- const numberOptions = [
42
- { text: 'One', value: 1 },
43
- { text: 'Two', value: 2 },
44
- { text: 'Three', value: 3 },
45
- { text: 'Four', value: 4 },
46
- { text: 'Five', value: 5 }
47
- ];
48
-
49
- // Track selected numbers
50
- const selectedNumbers = ref([]);
51
- const displayText = ref('');
52
-
53
- // Display the selection summary
54
- const displaySelection = computed(() => {
55
- if (selectedNumbers.value.length === 0) {
56
- return 'None';
57
- }
58
- return selectedNumbers.value.join(', ');
59
- });
60
-
61
- // Handle selected items and update display text
62
- const handleSelectedItems = (items) => {
63
- // For multi-select, update display text to show selected items
64
- const selectedTexts = items.map(itemValue => {
65
- // Find corresponding text for each selected value
66
- const option = numberOptions.find(opt => opt.value === itemValue);
67
- return option ? option.text : itemValue;
68
- });
69
-
70
- // Update the input display text
71
- displayText.value = selectedTexts.join(', ');
72
-
73
- console.log('Selected values:', items);
74
- console.log('Type of first value:', typeof items[0]);
75
- };
76
- </script>