datajunction-ui 0.0.27 → 0.0.30

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.
@@ -2,197 +2,117 @@ import { formatRelativeTime, getDateGroup, groupByDate } from '../date';
2
2
 
3
3
  describe('date utilities', () => {
4
4
  describe('formatRelativeTime', () => {
5
- it('returns "just now" for times less than 1 minute ago', () => {
5
+ it('returns "just now" for times less than a minute ago', () => {
6
6
  const now = new Date();
7
7
  expect(formatRelativeTime(now.toISOString())).toBe('just now');
8
-
9
- const thirtySecondsAgo = new Date(Date.now() - 30000);
10
- expect(formatRelativeTime(thirtySecondsAgo.toISOString())).toBe(
11
- 'just now',
12
- );
13
8
  });
14
9
 
15
- it('returns minutes ago for times less than 1 hour ago', () => {
16
- const fiveMinutesAgo = new Date(Date.now() - 5 * 60000);
17
- expect(formatRelativeTime(fiveMinutesAgo.toISOString())).toBe('5m ago');
18
-
19
- const thirtyMinutesAgo = new Date(Date.now() - 30 * 60000);
20
- expect(formatRelativeTime(thirtyMinutesAgo.toISOString())).toBe(
21
- '30m ago',
22
- );
23
-
24
- const fiftyNineMinutesAgo = new Date(Date.now() - 59 * 60000);
25
- expect(formatRelativeTime(fiftyNineMinutesAgo.toISOString())).toBe(
26
- '59m ago',
27
- );
10
+ it('returns minutes for times less than an hour ago', () => {
11
+ const date = new Date();
12
+ date.setMinutes(date.getMinutes() - 30);
13
+ expect(formatRelativeTime(date.toISOString())).toBe('30m ago');
28
14
  });
29
15
 
30
- it('returns hours ago for times less than 24 hours ago', () => {
31
- const oneHourAgo = new Date(Date.now() - 1 * 3600000);
32
- expect(formatRelativeTime(oneHourAgo.toISOString())).toBe('1h ago');
33
-
34
- const twelveHoursAgo = new Date(Date.now() - 12 * 3600000);
35
- expect(formatRelativeTime(twelveHoursAgo.toISOString())).toBe('12h ago');
36
-
37
- const twentyThreeHoursAgo = new Date(Date.now() - 23 * 3600000);
38
- expect(formatRelativeTime(twentyThreeHoursAgo.toISOString())).toBe(
39
- '23h ago',
40
- );
16
+ it('returns hours for times less than a day ago', () => {
17
+ const date = new Date();
18
+ date.setHours(date.getHours() - 5);
19
+ expect(formatRelativeTime(date.toISOString())).toBe('5h ago');
41
20
  });
42
21
 
43
- it('returns days ago for times less than 7 days ago', () => {
44
- const oneDayAgo = new Date(Date.now() - 1 * 86400000);
45
- expect(formatRelativeTime(oneDayAgo.toISOString())).toBe('1d ago');
46
-
47
- const threeDaysAgo = new Date(Date.now() - 3 * 86400000);
48
- expect(formatRelativeTime(threeDaysAgo.toISOString())).toBe('3d ago');
49
-
50
- const sixDaysAgo = new Date(Date.now() - 6 * 86400000);
51
- expect(formatRelativeTime(sixDaysAgo.toISOString())).toBe('6d ago');
22
+ it('returns days for times less than a week ago', () => {
23
+ const date = new Date();
24
+ date.setDate(date.getDate() - 3);
25
+ expect(formatRelativeTime(date.toISOString())).toBe('3d ago');
52
26
  });
53
27
 
54
- it('returns formatted date for times 7 or more days ago', () => {
55
- const sevenDaysAgo = new Date(Date.now() - 7 * 86400000);
56
- const result = formatRelativeTime(sevenDaysAgo.toISOString());
57
- // Should return a locale date string, not "Xd ago"
58
- expect(result).not.toContain('d ago');
59
- expect(result).toMatch(/\d/); // Should contain numbers (date)
28
+ it('returns formatted date for times more than a week ago', () => {
29
+ const date = new Date();
30
+ date.setDate(date.getDate() - 10);
31
+ const result = formatRelativeTime(date.toISOString());
32
+ expect(result).toMatch(/\d{1,2}\/\d{1,2}\/\d{4}/);
60
33
  });
61
34
  });
62
35
 
63
36
  describe('getDateGroup', () => {
64
- it('returns "Today" for dates from today', () => {
37
+ it('returns "Today" for today\'s date', () => {
65
38
  const now = new Date();
66
39
  expect(getDateGroup(now.toISOString())).toBe('Today');
67
-
68
- // Earlier today (midnight)
69
- const midnight = new Date();
70
- midnight.setHours(0, 0, 0, 0);
71
- expect(getDateGroup(midnight.toISOString())).toBe('Today');
72
40
  });
73
41
 
74
- it('returns "Yesterday" for dates from yesterday', () => {
42
+ it('returns "Yesterday" for yesterday\'s date', () => {
75
43
  const yesterday = new Date();
76
44
  yesterday.setDate(yesterday.getDate() - 1);
77
45
  expect(getDateGroup(yesterday.toISOString())).toBe('Yesterday');
78
46
  });
79
47
 
80
- it('returns "This Week" for dates from this week (but not today or yesterday)', () => {
48
+ it('returns "Last Week" for dates from last week', () => {
81
49
  const now = new Date();
82
50
  const dayOfWeek = now.getDay();
83
-
84
- // Only test if we're not on Sunday (0) or Monday (1)
85
- // because "This Week" starts on Sunday
86
- if (dayOfWeek >= 2) {
87
- const twoDaysAgo = new Date();
88
- twoDaysAgo.setDate(twoDaysAgo.getDate() - 2);
89
- expect(getDateGroup(twoDaysAgo.toISOString())).toBe('This Week');
90
- }
51
+ const lastWeek = new Date();
52
+ lastWeek.setDate(lastWeek.getDate() - dayOfWeek - 3); // Go to last week
53
+ expect(getDateGroup(lastWeek.toISOString())).toBe('Last Week');
91
54
  });
92
55
 
93
- it('returns "Last Week" for dates from last week', () => {
94
- const now = new Date();
95
- const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
96
- const dayOfWeek = today.getDay();
97
-
98
- // Calculate start of this week (Sunday)
99
- const thisWeekStart = new Date(today);
100
- thisWeekStart.setDate(thisWeekStart.getDate() - dayOfWeek);
56
+ it('returns "Older" for dates more than two weeks ago', () => {
57
+ const oldDate = new Date();
58
+ oldDate.setDate(oldDate.getDate() - 20);
59
+ expect(getDateGroup(oldDate.toISOString())).toBe('Older');
60
+ });
61
+ });
101
62
 
102
- // Last week is 1-7 days before this week's start
103
- const lastWeekDate = new Date(thisWeekStart);
104
- lastWeekDate.setDate(lastWeekDate.getDate() - 3); // Middle of last week
63
+ describe('getDateGroup with mocked time', () => {
64
+ // Test "This Week" branch with a fixed date (Wednesday, Jan 15, 2025)
65
+ beforeEach(() => {
66
+ jest.useFakeTimers();
67
+ jest.setSystemTime(new Date('2025-01-15T12:00:00Z'));
68
+ });
105
69
 
106
- expect(getDateGroup(lastWeekDate.toISOString())).toBe('Last Week');
70
+ afterEach(() => {
71
+ jest.useRealTimers();
107
72
  });
108
73
 
109
- it('returns "Older" for dates older than last week', () => {
110
- const threeWeeksAgo = new Date();
111
- threeWeeksAgo.setDate(threeWeeksAgo.getDate() - 21);
112
- expect(getDateGroup(threeWeeksAgo.toISOString())).toBe('Older');
74
+ it('returns "This Week" for dates earlier this week', () => {
75
+ // Jan 15, 2025 is Wednesday. Week starts Sunday Jan 12.
76
+ // A date from Monday Jan 13 should be "This Week"
77
+ const thisWeekDate = new Date('2025-01-13T12:00:00Z');
78
+ expect(getDateGroup(thisWeekDate.toISOString())).toBe('This Week');
79
+ });
113
80
 
114
- const monthAgo = new Date();
115
- monthAgo.setMonth(monthAgo.getMonth() - 1);
116
- expect(getDateGroup(monthAgo.toISOString())).toBe('Older');
81
+ it('returns "This Week" for Sunday of the current week', () => {
82
+ // Sunday Jan 12, 2025 is the start of this week
83
+ const sundayThisWeek = new Date('2025-01-12T12:00:00Z');
84
+ expect(getDateGroup(sundayThisWeek.toISOString())).toBe('This Week');
117
85
  });
118
86
  });
119
87
 
120
88
  describe('groupByDate', () => {
121
- it('groups items by their date', () => {
89
+ it('groups items by date', () => {
122
90
  const now = new Date();
123
91
  const yesterday = new Date();
124
92
  yesterday.setDate(yesterday.getDate() - 1);
125
- const lastMonth = new Date();
126
- lastMonth.setMonth(lastMonth.getMonth() - 1);
127
93
 
128
94
  const items = [
129
95
  { id: 1, created_at: now.toISOString() },
130
- { id: 2, created_at: now.toISOString() },
131
- { id: 3, created_at: yesterday.toISOString() },
132
- { id: 4, created_at: lastMonth.toISOString() },
96
+ { id: 2, created_at: yesterday.toISOString() },
133
97
  ];
134
98
 
135
- const result = groupByDate(items);
136
-
137
- expect(result).toHaveLength(3); // Today, Yesterday, Older
138
-
139
- const todayGroup = result.find(g => g.label === 'Today');
140
- expect(todayGroup.items).toHaveLength(2);
141
- expect(todayGroup.items.map(i => i.id)).toEqual([1, 2]);
142
-
143
- const yesterdayGroup = result.find(g => g.label === 'Yesterday');
144
- expect(yesterdayGroup.items).toHaveLength(1);
145
- expect(yesterdayGroup.items[0].id).toBe(3);
146
-
147
- const olderGroup = result.find(g => g.label === 'Older');
148
- expect(olderGroup.items).toHaveLength(1);
149
- expect(olderGroup.items[0].id).toBe(4);
150
- });
151
-
152
- it('returns groups in correct order', () => {
153
- const now = new Date();
154
- const yesterday = new Date();
155
- yesterday.setDate(yesterday.getDate() - 1);
156
- const lastMonth = new Date();
157
- lastMonth.setMonth(lastMonth.getMonth() - 1);
158
-
159
- // Items in random order
160
- const items = [
161
- { id: 1, created_at: lastMonth.toISOString() },
162
- { id: 2, created_at: now.toISOString() },
163
- { id: 3, created_at: yesterday.toISOString() },
164
- ];
165
-
166
- const result = groupByDate(items);
167
- const labels = result.map(g => g.label);
168
-
169
- // Should be in order: Today, Yesterday, Older
170
- expect(labels).toEqual(['Today', 'Yesterday', 'Older']);
171
- });
172
-
173
- it('uses custom date field when specified', () => {
174
- const now = new Date();
175
- const items = [{ id: 1, updated_at: now.toISOString() }];
176
-
177
- const result = groupByDate(items, 'updated_at');
178
-
179
- expect(result).toHaveLength(1);
180
- expect(result[0].label).toBe('Today');
99
+ const groups = groupByDate(items);
100
+ expect(groups.length).toBeGreaterThan(0);
101
+ expect(groups[0].label).toBe('Today');
181
102
  });
182
103
 
183
104
  it('returns empty array for empty input', () => {
184
- const result = groupByDate([]);
185
- expect(result).toEqual([]);
105
+ const groups = groupByDate([]);
106
+ expect(groups).toEqual([]);
186
107
  });
187
108
 
188
- it('only includes groups that have items', () => {
109
+ it('uses custom date field', () => {
189
110
  const now = new Date();
190
- const items = [{ id: 1, created_at: now.toISOString() }];
191
-
192
- const result = groupByDate(items);
111
+ const items = [{ id: 1, updated_at: now.toISOString() }];
193
112
 
194
- expect(result).toHaveLength(1);
195
- expect(result[0].label).toBe('Today');
113
+ const groups = groupByDate(items, 'updated_at');
114
+ expect(groups.length).toBe(1);
115
+ expect(groups[0].label).toBe('Today');
196
116
  });
197
117
  });
198
118
  });
@@ -254,6 +254,45 @@ tr {
254
254
  border-color: #12263f;
255
255
  }
256
256
 
257
+ .nodes-table-body tr:hover td {
258
+ background-color: rgba(59, 130, 246, 0.03);
259
+ }
260
+
261
+ /* Sortable table headers */
262
+ button.sortable {
263
+ background: none;
264
+ border: none;
265
+ cursor: pointer;
266
+ color: inherit;
267
+ padding: 0;
268
+ display: inline-flex;
269
+ align-items: center;
270
+ gap: 4px;
271
+ transition: color 0.15s ease;
272
+ }
273
+
274
+ button.sortable:hover {
275
+ color: #1e293b;
276
+ }
277
+
278
+ button.sortable::after {
279
+ content: '↕';
280
+ font-size: 12px;
281
+ opacity: 0.8;
282
+ }
283
+
284
+ button.sortable.ascending::after {
285
+ content: '↑';
286
+ opacity: 1;
287
+ color: #3b82f6;
288
+ }
289
+
290
+ button.sortable.descending::after {
291
+ content: '↓';
292
+ opacity: 1;
293
+ color: #3b82f6;
294
+ }
295
+
257
296
  .card {
258
297
  box-shadow: 0 0.75rem 1.5rem rgba(18, 38, 63, 0.03);
259
298
  position: relative;
@@ -964,23 +1003,25 @@ pre {
964
1003
  }
965
1004
 
966
1005
  .select-name {
967
- margin-top: 5px;
968
- padding: 7px 7px;
969
- width: fit-content;
1006
+ display: flex;
1007
+ align-items: center;
1008
+ gap: 8px;
1009
+ padding: 8px 12px;
1010
+ cursor: pointer;
1011
+ user-select: none;
1012
+ transition: background 0.15s;
1013
+ font-size: 14px;
1014
+ font-weight: 400;
1015
+ color: #1e293b;
970
1016
  border-radius: 4px;
971
- cursor: default;
972
- font-size: 0.9125rem;
973
- color: #95aac9;
974
1017
  }
975
1018
 
976
1019
  .select-name:hover {
977
- background-color: #bdbaba3c;
1020
+ background-color: #f1f5f9;
978
1021
  }
979
1022
 
980
1023
  .select-name-highlight {
981
- font-weight: 600;
982
- background-color: #bdbaba3c;
983
- border: 1px solid rgba(94, 94, 94, 0.24);
1024
+ background-color: #eef1f6;
984
1025
  }
985
1026
 
986
1027
  .inactive {