@uxf/core 11.85.0 → 11.87.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/README.md CHANGED
@@ -285,6 +285,42 @@ import { filterNullish } from "@uxf/core/utils/filter-nullish";
285
285
  filterNullish([0, "text", null, undefined, [], {}]); /* returns [0, "text", [], {}] */
286
286
  ```
287
287
 
288
+ ## filterAriaAndDataAttrs
289
+
290
+ Filters an object to return only properties that start with `aria-` or `data-` prefixes. This utility is useful when you need to pass accessibility and data attributes to HTML elements while excluding other props like event handlers or component-specific props.
291
+
292
+ ```tsx
293
+ import { filterAriaAndDataAttrs } from "@uxf/core/utils/filter-aria-and-data-attrs";
294
+
295
+ // Filter accessibility and data attributes from component props
296
+ const props = {
297
+ "aria-label": "Close button",
298
+ "data-testid": "close-btn",
299
+ className: "button",
300
+ onClick: handleClick,
301
+ };
302
+
303
+ const htmlAttrs = filterAriaAndDataAttrs(props);
304
+ // Result: { "aria-label": "Close button", "data-testid": "close-btn" }
305
+
306
+ <button {...htmlAttrs}>Close</button>
307
+ ```
308
+
309
+ ```tsx
310
+ // Use case: Passing only safe attributes to a native element
311
+ function CustomInput({ label, onChange, ...restProps }) {
312
+ const accessibilityAttrs = filterAriaAndDataAttrs(restProps);
313
+
314
+ return <input {...accessibilityAttrs} onChange={onChange} />;
315
+ }
316
+
317
+ <CustomInput
318
+ aria-describedby="helper-text"
319
+ data-analytics="email-input"
320
+ customProp="ignored"
321
+ />
322
+ ```
323
+
288
324
  ### formatBytes
289
325
 
290
326
  Appends suitable unit to the byte value of data size.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uxf/core",
3
- "version": "11.85.0",
3
+ "version": "11.87.0",
4
4
  "description": "UXF Core",
5
5
  "author": "Petr Vejvoda <vejvoda@uxf.cz>",
6
6
  "homepage": "https://gitlab.com/uxf-npm/core#readme",
@@ -0,0 +1,3 @@
1
+ export declare function filterAriaAndDataAttrs<P extends Record<string, any>>(props: P): {
2
+ [k: string]: any;
3
+ };
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.filterAriaAndDataAttrs = filterAriaAndDataAttrs;
4
+ function filterAriaAndDataAttrs(props) {
5
+ return Object.fromEntries(Object.entries(props).filter((p) => { var _a, _b; return Boolean(((_a = p.at(0)) === null || _a === void 0 ? void 0 : _a.startsWith("aria-")) || ((_b = p.at(0)) === null || _b === void 0 ? void 0 : _b.startsWith("data-"))); }));
6
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const filter_aria_and_data_attrs_1 = require("./filter-aria-and-data-attrs");
4
+ describe("filterAriaAndDataAttrs", () => {
5
+ it("should filter only aria- attributes", () => {
6
+ const props = {
7
+ "aria-label": "test label",
8
+ "aria-hidden": true,
9
+ className: "test-class",
10
+ id: "test-id",
11
+ };
12
+ const result = (0, filter_aria_and_data_attrs_1.filterAriaAndDataAttrs)(props);
13
+ expect(result).toEqual({
14
+ "aria-label": "test label",
15
+ "aria-hidden": true,
16
+ });
17
+ });
18
+ it("should filter only data- attributes", () => {
19
+ const props = {
20
+ "data-testid": "test",
21
+ "data-value": 123,
22
+ className: "test-class",
23
+ onClick: jest.fn(),
24
+ };
25
+ const result = (0, filter_aria_and_data_attrs_1.filterAriaAndDataAttrs)(props);
26
+ expect(result).toEqual({
27
+ "data-testid": "test",
28
+ "data-value": 123,
29
+ });
30
+ });
31
+ it("should filter both aria- and data- attributes", () => {
32
+ const props = {
33
+ "aria-label": "label",
34
+ "data-testid": "test",
35
+ "aria-describedby": "desc",
36
+ "data-custom": "value",
37
+ className: "class",
38
+ style: { color: "red" },
39
+ };
40
+ const result = (0, filter_aria_and_data_attrs_1.filterAriaAndDataAttrs)(props);
41
+ expect(result).toEqual({
42
+ "aria-label": "label",
43
+ "data-testid": "test",
44
+ "aria-describedby": "desc",
45
+ "data-custom": "value",
46
+ });
47
+ });
48
+ it("should return empty object when no aria- or data- attributes present", () => {
49
+ const props = {
50
+ className: "test",
51
+ id: "id",
52
+ onClick: jest.fn(),
53
+ };
54
+ const result = (0, filter_aria_and_data_attrs_1.filterAriaAndDataAttrs)(props);
55
+ expect(result).toEqual({});
56
+ });
57
+ it("should return empty object for empty input", () => {
58
+ const props = {};
59
+ const result = (0, filter_aria_and_data_attrs_1.filterAriaAndDataAttrs)(props);
60
+ expect(result).toEqual({});
61
+ });
62
+ it("should handle attributes with various value types", () => {
63
+ const props = {
64
+ "aria-expanded": false,
65
+ "aria-level": 2,
66
+ "data-object": { nested: "value" },
67
+ "data-array": [1, 2, 3],
68
+ "data-null": null,
69
+ "data-undefined": undefined,
70
+ regularProp: "value",
71
+ };
72
+ const result = (0, filter_aria_and_data_attrs_1.filterAriaAndDataAttrs)(props);
73
+ expect(result).toEqual({
74
+ "aria-expanded": false,
75
+ "aria-level": 2,
76
+ "data-object": { nested: "value" },
77
+ "data-array": [1, 2, 3],
78
+ "data-null": null,
79
+ "data-undefined": undefined,
80
+ });
81
+ });
82
+ it("should not filter attributes that contain but don't start with aria- or data-", () => {
83
+ const props = {
84
+ "aria-label": "keep",
85
+ "data-id": "keep",
86
+ "my-aria-label": "remove",
87
+ "my-data-id": "remove",
88
+ };
89
+ const result = (0, filter_aria_and_data_attrs_1.filterAriaAndDataAttrs)(props);
90
+ expect(result).toEqual({
91
+ "aria-label": "keep",
92
+ "data-id": "keep",
93
+ });
94
+ });
95
+ });
package/utils/resizer.js CHANGED
@@ -35,7 +35,11 @@ const BASE_PATH = (_b = process.env.NEXT_PUBLIC_BASE_PATH) !== null && _b !== vo
35
35
  const resizerStaticImageUrl = (src, width = "auto", height = "auto", props = {}, version = 1) => {
36
36
  var _a;
37
37
  const { fit = "cover", position = "center", toFormat = undefined, trim = "not-trim", background = "FFF" } = props;
38
- const filepath = typeof src === "string" ? src : src.src;
38
+ let filepath = typeof src === "string" ? src : src.src;
39
+ // fix absolute assetPrefix
40
+ if (!filepath.startsWith("/")) {
41
+ filepath = new URL(filepath).pathname;
42
+ }
39
43
  const dotIndex = filepath.lastIndexOf(".");
40
44
  const filename = filepath.slice(0, dotIndex);
41
45
  const extension = filepath.slice(dotIndex + 1);
@@ -16,7 +16,7 @@ describe("resizerGetDefaultConfig", () => {
16
16
  toFormat: "avif",
17
17
  quality: 1,
18
18
  });
19
- expect(url).toBe("/generated/static/x_x_cv_b_t_nt_1/1http://example.com/image.jpg.avif");
19
+ expect(url).toBe("/generated/static/x_x_cv_b_t_nt_1/1/image.jpg.avif");
20
20
  });
21
21
  it("should return a valid URL for an ImageResponse source", () => {
22
22
  const imageResponse = {
@@ -29,7 +29,7 @@ describe("resizerGetDefaultConfig", () => {
29
29
  });
30
30
  it("should return a valid URL for a string source", () => {
31
31
  const url = (0, resizer_1.resizerImageUrl)("http://example.com/image.png", 250, 250, { toFormat: "webp" });
32
- expect(url).toBe("/generated/static/250_250_cv_c_FFF_nt_x/1http://example.com/image.png.webp");
32
+ expect(url).toBe("/generated/static/250_250_cv_c_FFF_nt_x/1/image.png.webp");
33
33
  });
34
34
  it("should return undefined for null or undefined source", () => {
35
35
  expect((0, resizer_1.resizerImageUrl)(null)).toBeUndefined();