Skip to content

Commit 0461b36

Browse files
committed
Update sub_button_wheel to v1.3.0
1 parent 517f77b commit 0461b36

File tree

8 files changed

+1133
-527
lines changed

8 files changed

+1133
-527
lines changed
Lines changed: 161 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,175 @@
11
import { toArray } from "../arrays.js";
22

33
describe("toArray", () => {
4-
test("returns array as-is when input is already an array", () => {
5-
const input = [1, 2, 3];
6-
expect(toArray(input)).toBe(input); // same reference
7-
});
4+
describe("Standard JavaScript arrays", () => {
5+
test("returns array as-is when input is already an array", () => {
6+
const input = [1, 2, 3];
7+
expect(toArray(input)).toBe(input); // same reference
8+
});
89

9-
test("converts object values to array", () => {
10-
const input = { a: 1, b: 2, c: 3 };
11-
expect(toArray(input)).toEqual([1, 2, 3]);
10+
test("returns empty array for empty array", () => {
11+
const input = [];
12+
expect(toArray(input)).toBe(input);
13+
});
1214
});
1315

14-
test("returns empty array for empty object", () => {
15-
expect(toArray({})).toEqual([]);
16+
describe("Standard YAML array format (from YAML editor)", () => {
17+
test("converts single configuration object to array", () => {
18+
const input = {
19+
sub_button: 2,
20+
close_on_click: true,
21+
};
22+
expect(toArray(input)).toEqual([{ sub_button: 2, close_on_click: true }]);
23+
});
24+
25+
test("wraps nested configuration object in array", () => {
26+
const input = {
27+
wheel_opener: "main",
28+
wheel_buttons: [
29+
{ sub_button: 2, close_on_click: true },
30+
{ sub_button: 3, close_on_click: true },
31+
],
32+
};
33+
expect(toArray(input)).toEqual([
34+
{
35+
wheel_opener: "main",
36+
wheel_buttons: [
37+
{ sub_button: 2, close_on_click: true },
38+
{ sub_button: 3, close_on_click: true },
39+
],
40+
},
41+
]);
42+
});
1643
});
1744

18-
test("returns empty array for null input", () => {
19-
expect(toArray(null)).toEqual([]);
45+
describe("Bubble Card UI editor format (numbered object keys)", () => {
46+
test("converts numbered object keys to array", () => {
47+
const input = {
48+
0: { sub_button: 2, close_on_click: true },
49+
1: { sub_button: 3, close_on_click: true },
50+
};
51+
expect(toArray(input)).toEqual([
52+
{ sub_button: 2, close_on_click: true },
53+
{ sub_button: 3, close_on_click: true },
54+
]);
55+
});
56+
57+
test("converts numbered object keys with gaps to array", () => {
58+
const input = {
59+
0: { sub_button: 1, close_on_click: true },
60+
2: { sub_button: 3, close_on_click: false },
61+
4: { sub_button: 5, close_on_click: true },
62+
};
63+
expect(toArray(input)).toEqual([
64+
{ sub_button: 1, close_on_click: true },
65+
{ sub_button: 3, close_on_click: false },
66+
{ sub_button: 5, close_on_click: true },
67+
]);
68+
});
69+
70+
test("handles single numbered key as array", () => {
71+
const input = {
72+
0: { sub_button: 1, close_on_click: true },
73+
};
74+
expect(toArray(input)).toEqual([{ sub_button: 1, close_on_click: true }]);
75+
});
76+
77+
test("handles non-sequential numbered keys", () => {
78+
const input = {
79+
5: { sub_button: 6 },
80+
1: { sub_button: 2 },
81+
3: { sub_button: 4 },
82+
};
83+
// Should preserve order by key value when converting
84+
expect(toArray(input)).toEqual([{ sub_button: 2 }, { sub_button: 4 }, { sub_button: 6 }]);
85+
});
2086
});
2187

22-
test("returns empty array for undefined input", () => {
23-
expect(toArray(undefined)).toEqual([]);
88+
describe("Mixed and edge cases", () => {
89+
test("handles object with mixed key types - treats as single object", () => {
90+
const input = {
91+
0: { sub_button: 1 },
92+
name: "test",
93+
wheel_opener: "main",
94+
};
95+
// Mixed keys should be treated as single object, not numbered array
96+
expect(toArray(input)).toEqual([
97+
{
98+
0: { sub_button: 1 },
99+
name: "test",
100+
wheel_opener: "main",
101+
},
102+
]);
103+
});
104+
105+
test("returns empty array for null input", () => {
106+
expect(toArray(null)).toEqual([]);
107+
});
108+
109+
test("returns empty array for undefined input", () => {
110+
expect(toArray(undefined)).toEqual([]);
111+
});
112+
113+
test("returns empty array for non-object, non-array values", () => {
114+
expect(toArray(42)).toEqual([]);
115+
expect(toArray("hello")).toEqual([]);
116+
expect(toArray(true)).toEqual([]);
117+
});
118+
119+
test("returns single array for empty object", () => {
120+
expect(toArray({})).toEqual([{}]);
121+
});
24122
});
25123

26-
test("returns empty array for non-object, non-array values", () => {
27-
expect(toArray(42)).toEqual([]);
28-
expect(toArray("hello")).toEqual([]);
29-
expect(toArray(true)).toEqual([]);
124+
describe("Real-world bubble card configurations", () => {
125+
test("handles wheel_buttons from YAML editor", () => {
126+
const input = [
127+
{ sub_button: 2, close_on_click: true },
128+
{ sub_button: 3, close_on_click: true },
129+
];
130+
expect(toArray(input)).toBe(input);
131+
});
132+
133+
test("handles wheel_buttons from UI editor", () => {
134+
const input = {
135+
0: { sub_button: 2, close_on_click: true },
136+
1: { sub_button: 3, close_on_click: true },
137+
};
138+
expect(toArray(input)).toEqual([
139+
{ sub_button: 2, close_on_click: true },
140+
{ sub_button: 3, close_on_click: true },
141+
]);
142+
});
143+
144+
test("handles single wheel configuration from YAML editor", () => {
145+
const input = {
146+
wheel_opener: "main",
147+
wheel_buttons: [{ sub_button: 1 }, { sub_button: 2 }],
148+
};
149+
expect(toArray(input)).toEqual([input]);
150+
});
151+
152+
test("handles multiple wheel configurations from UI editor", () => {
153+
const input = {
154+
0: {
155+
wheel_opener: "main",
156+
wheel_buttons: [{ sub_button: 1 }, { sub_button: 2 }],
157+
},
158+
1: {
159+
wheel_opener: "3",
160+
wheel_buttons: [{ sub_button: 4 }, { sub_button: 5 }],
161+
},
162+
};
163+
expect(toArray(input)).toEqual([
164+
{
165+
wheel_opener: "main",
166+
wheel_buttons: [{ sub_button: 1 }, { sub_button: 2 }],
167+
},
168+
{
169+
wheel_opener: "3",
170+
wheel_buttons: [{ sub_button: 4 }, { sub_button: 5 }],
171+
},
172+
]);
173+
});
30174
});
31175
});

modules/helpers/utils/arrays.js

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,30 @@
11
/**
22
* Converts the provided object or value to an array.
33
* If the input is already an array, it is returned as-is.
4-
* If the input is an object, its values are extracted into an array.
4+
* If the input is an object with all numeric keys (UI editor format), converts to array.
5+
* If the input is a regular object, it is wrapped in an array.
56
*
67
* @param {Object|Array} object - The object or array to convert to an array.
78
* @return {Array} The resulting array obtained from the input.
89
*/
910
export function toArray(object) {
1011
if (Array.isArray(object)) return object;
1112
if (!object || typeof object !== "object") return [];
13+
const keys = Object.keys(object);
1214

13-
return Object.values(object);
15+
// Empty object - wrap in array
16+
if (keys.length === 0) return [object];
17+
18+
// Check if ALL keys are numeric strings (UI editor format)
19+
const allNumeric = keys.every((key) => /^\d+$/.test(key));
20+
21+
if (allNumeric) {
22+
// Convert numbered object to array, sorted by numeric value
23+
return keys
24+
.map((key) => parseInt(key))
25+
.sort((a, b) => a - b)
26+
.map((index) => object[index.toString()]);
27+
}
28+
// Regular object - wrap in array
29+
return [object];
1430
}

modules/sub_button_wheel/CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog
22

3+
### v1.3.0
4+
5+
<details><summary>See Changes</summary>
6+
7+
- Added support for having multiple wheels in the same card. Each wheel can have its own configuration.
8+
9+
</details>
10+
311
### v1.2.0
412

513
<details><summary>See Changes</summary>
@@ -8,6 +16,7 @@
816
- Existing tap actions will automatically be removed from the wheel opener button without manual configuration.
917

1018
</details>
19+
1120
### v1.1.0
1221

1322
<details><summary>See Changes</summary>

modules/sub_button_wheel/README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,35 @@ sub_button_wheel:
239239
240240
</details>
241241
242+
<details>
243+
<summary><strong>Multiple wheels in the same card</strong></summary>
244+
245+
```yaml
246+
sub_button_wheel:
247+
- wheel_opener: sub-button-1
248+
layout_options:
249+
wheel_layout: even-circle
250+
animation_options:
251+
wheel_animation: instant
252+
animation_delay: 0.1
253+
animation_duration: 0.2
254+
wheel_buttons:
255+
- sub_button: 2
256+
- sub_button: 3
257+
- wheel_opener: sub-button-4
258+
layout_options:
259+
wheel_layout: even-circle
260+
animation_options:
261+
wheel_animation: instant
262+
animation_delay: 0.1
263+
animation_duration: 0.2
264+
wheel_buttons:
265+
- sub_button: 5
266+
- sub_button: 6
267+
```
268+
269+
</details>
270+
242271
## Install this module
243272
244273
1. Install [Bubble Card](https://github.com/Clooos/Bubble-Card) in Home Assistant if you haven't already.

0 commit comments

Comments
 (0)