@jobber/components-native 0.101.11 → 0.102.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 (59) hide show
  1. package/dist/docs/FormatFile/FormatFile.md +28 -2
  2. package/dist/docs/Icon/Icon.md +1 -0
  3. package/dist/package.json +3 -3
  4. package/dist/src/FormatFile/FormatFile.js +2 -2
  5. package/dist/src/FormatFile/FormatFileThumbnail.js +3 -2
  6. package/dist/src/FormatFile/FormatFileThumbnail.test.js +23 -0
  7. package/dist/src/FormatFile/components/FileView/FileView.js +6 -23
  8. package/dist/src/FormatFile/components/FileView/FileView.style.js +5 -0
  9. package/dist/src/FormatFile/components/MediaView/MediaView.js +40 -6
  10. package/dist/src/FormatFile/components/MediaView/MediaView.test.js +69 -0
  11. package/dist/src/FormatFile/constants.js +60 -0
  12. package/dist/src/FormatFile/index.js +1 -0
  13. package/dist/src/FormatFile/utils/getFileCategory.js +62 -0
  14. package/dist/src/FormatFile/utils/getFileCategory.test.js +83 -0
  15. package/dist/src/FormatFile/utils/index.js +4 -0
  16. package/dist/src/FormatFile/utils/mapFileTypeToIconName.js +31 -0
  17. package/dist/src/FormatFile/utils/mapFileTypeToIconName.test.js +41 -0
  18. package/dist/src/FormatFile/utils/sanitizeFileName.js +9 -0
  19. package/dist/src/FormatFile/utils/sanitizeFileName.test.js +18 -0
  20. package/dist/src/FormatFile/utils/truncateFileNameForLine.js +58 -0
  21. package/dist/src/FormatFile/utils/truncateFileNameForLine.test.js +40 -0
  22. package/dist/tsconfig.build.tsbuildinfo +1 -1
  23. package/dist/types/src/FormatFile/FormatFile.d.ts +11 -3
  24. package/dist/types/src/FormatFile/FormatFileThumbnail.d.ts +10 -2
  25. package/dist/types/src/FormatFile/components/FileView/FileView.d.ts +2 -1
  26. package/dist/types/src/FormatFile/components/FileView/FileView.style.d.ts +2 -0
  27. package/dist/types/src/FormatFile/components/MediaView/MediaView.d.ts +2 -1
  28. package/dist/types/src/FormatFile/constants.d.ts +29 -0
  29. package/dist/types/src/FormatFile/index.d.ts +2 -0
  30. package/dist/types/src/FormatFile/utils/getFileCategory.d.ts +28 -0
  31. package/dist/types/src/FormatFile/utils/getFileCategory.test.d.ts +1 -0
  32. package/dist/types/src/FormatFile/utils/index.d.ts +5 -0
  33. package/dist/types/src/FormatFile/utils/mapFileTypeToIconName.d.ts +18 -0
  34. package/dist/types/src/FormatFile/utils/mapFileTypeToIconName.test.d.ts +1 -0
  35. package/dist/types/src/FormatFile/utils/sanitizeFileName.d.ts +5 -0
  36. package/dist/types/src/FormatFile/utils/sanitizeFileName.test.d.ts +1 -0
  37. package/dist/types/src/FormatFile/utils/truncateFileNameForLine.d.ts +24 -0
  38. package/dist/types/src/FormatFile/utils/truncateFileNameForLine.test.d.ts +1 -0
  39. package/package.json +3 -3
  40. package/src/FormatFile/FormatFile.stories.tsx +41 -1
  41. package/src/FormatFile/FormatFile.tsx +14 -2
  42. package/src/FormatFile/FormatFileThumbnail.test.tsx +44 -0
  43. package/src/FormatFile/FormatFileThumbnail.tsx +13 -1
  44. package/src/FormatFile/components/FileView/FileView.style.ts +5 -0
  45. package/src/FormatFile/components/FileView/FileView.tsx +12 -29
  46. package/src/FormatFile/components/MediaView/MediaView.test.tsx +159 -2
  47. package/src/FormatFile/components/MediaView/MediaView.tsx +114 -5
  48. package/src/FormatFile/constants.ts +64 -0
  49. package/src/FormatFile/index.ts +7 -0
  50. package/src/FormatFile/utils/getFileCategory.test.ts +96 -0
  51. package/src/FormatFile/utils/getFileCategory.ts +106 -0
  52. package/src/FormatFile/utils/index.ts +5 -0
  53. package/src/FormatFile/utils/mapFileTypeToIconName.test.ts +43 -0
  54. package/src/FormatFile/utils/mapFileTypeToIconName.ts +42 -0
  55. package/src/FormatFile/utils/sanitizeFileName.test.ts +25 -0
  56. package/src/FormatFile/utils/sanitizeFileName.ts +9 -0
  57. package/src/FormatFile/utils/truncateFileNameForLine.test.ts +61 -0
  58. package/src/FormatFile/utils/truncateFileNameForLine.ts +68 -0
  59. package/src/ThumbnailList/__snapshots__/ThumbnailList.test.tsx.snap +1 -0
@@ -0,0 +1,41 @@
1
+ import { mapFileTypeToIconName } from "./mapFileTypeToIconName";
2
+ describe("mapFileTypeToIconName", () => {
3
+ it("returns 'alert' when neither name nor type is provided", () => {
4
+ expect(mapFileTypeToIconName({})).toBe("alert");
5
+ });
6
+ it.each([
7
+ ["application/pdf", undefined, "pdf"],
8
+ [undefined, "report.pdf", "pdf"],
9
+ [undefined, "report.doc", "word"],
10
+ [undefined, "report.docx", "word"],
11
+ [undefined, "report.odt", "word"],
12
+ [undefined, "report.fodt", "word"],
13
+ [undefined, "memo.rtf", "word"],
14
+ [undefined, "notes.pages", "word"],
15
+ ["application/vnd.ms-excel", undefined, "excel"],
16
+ [undefined, "data.xls", "excel"],
17
+ [undefined, "data.xlsx", "excel"],
18
+ [undefined, "data.ods", "excel"],
19
+ [undefined, "data.numbers", "excel"],
20
+ [undefined, "data.csv", "excel"],
21
+ ["video/mp4", undefined, "videoFile"],
22
+ ["video/quicktime", undefined, "videoFile"],
23
+ [undefined, "clip.mp4", "videoFile"],
24
+ [undefined, "clip.mov", "videoFile"],
25
+ [undefined, "clip.MOV", "videoFile"],
26
+ [undefined, "clip.webm", "videoFile"],
27
+ [undefined, "clip.mkv", "videoFile"],
28
+ [undefined, "clip.avi", "videoFile"],
29
+ [undefined, "clip.m4v", "videoFile"],
30
+ [undefined, "clip.wmv", "videoFile"],
31
+ [undefined, "clip.flv", "videoFile"],
32
+ [undefined, "clip.mpg", "videoFile"],
33
+ [undefined, "clip.mpeg", "videoFile"],
34
+ [undefined, "clip.3gp", "videoFile"],
35
+ ["application/octet-stream", "unknown.bin", "file"],
36
+ [undefined, "no-extension", "file"],
37
+ [undefined, "presentation.pptx", "file"],
38
+ ])("maps type=%s fileName=%s → %s", (fileType, fileName, expected) => {
39
+ expect(mapFileTypeToIconName({ fileType, fileName })).toBe(expected);
40
+ });
41
+ });
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Collapse CR / LF in a filename to a single space so embedded line breaks
3
+ * never reach the rendered `Text` and cause vertical layout drift.
4
+ */
5
+ export function sanitizeFileName(name) {
6
+ if (!name)
7
+ return "";
8
+ return name.replace(/[\r\n]+/g, " ");
9
+ }
@@ -0,0 +1,18 @@
1
+ import { sanitizeFileName } from "./sanitizeFileName";
2
+ describe("sanitizeFileName", () => {
3
+ it("returns an empty string when input is undefined", () => {
4
+ expect(sanitizeFileName(undefined)).toBe("");
5
+ });
6
+ it("returns the input unchanged when no newlines are present", () => {
7
+ expect(sanitizeFileName("report.pdf")).toBe("report.pdf");
8
+ });
9
+ it("collapses a single embedded \\n into a space", () => {
10
+ expect(sanitizeFileName("first\nsecond.txt")).toBe("first second.txt");
11
+ });
12
+ it("collapses runs of CR / LF into a single space", () => {
13
+ expect(sanitizeFileName("first\r\n\r\nsecond.txt")).toBe("first second.txt");
14
+ });
15
+ it("leaves spaces unchanged", () => {
16
+ expect(sanitizeFileName("with spaces.pdf")).toBe("with spaces.pdf");
17
+ });
18
+ });
@@ -0,0 +1,58 @@
1
+ const DEFAULT_MAX_LENGTH = 15;
2
+ const MIN_MAX_LENGTH = 2;
3
+ const ELLIPSIS = "…";
4
+ /**
5
+ * Maximum total length (including the leading dot) of a trailing token that
6
+ * we are willing to treat as a file extension. 10 covers every real-world
7
+ * extension this package routes to a recognisable icon (`.numbers`, `.fodt`,
8
+ * `.docx`, `.xlsx`, etc.) while rejecting accidental long suffixes from
9
+ * URL-shaped filenames. Chained pseudo-extensions like `.tar.gz` collapse
10
+ * to the trailing portion (`.gz`), which is the common-sense user
11
+ * expectation.
12
+ */
13
+ const MAX_EXTENSION_LENGTH = 10;
14
+ /**
15
+ * Truncate a filename so it fits a single line in the small file-type tile.
16
+ *
17
+ * Why this exists: on Android, RN's `Text` with `numberOfLines={1}` does not
18
+ * always enforce single-line layout for URL-shaped strings with no
19
+ * whitespace (e.g. `vertopal.com/converter`). They wrap to a second line
20
+ * which then bleeds outside the rounded tile border. Pre-truncating in JS
21
+ * guarantees the rendered string is short enough that single-line wrapping
22
+ * is never required.
23
+ *
24
+ * The truncation preserves the trailing file extension when one is
25
+ * detectable so the file type stays visible at a glance (examples below
26
+ * use the default 15-character budget):
27
+ * `vertopal.com/converter` → `vertopal.com/c…`
28
+ * `file-sample_2_dx.docx` → `file-samp….docx`
29
+ * `quarterly-budget.numbers` → `quarte….numbers`
30
+ * `report.pdf` → `report.pdf` (unchanged)
31
+ * `embedded\nname.txt` → caller should pass through
32
+ * `sanitizeFileName` first
33
+ *
34
+ * `maxLength` is clamped to a sensible minimum of 2 so a caller passing
35
+ * `0` or a negative value cannot violate the single-line guarantee.
36
+ */
37
+ // eslint-disable-next-line max-statements
38
+ export function truncateFileNameForLine(name, maxLength = DEFAULT_MAX_LENGTH) {
39
+ if (!name)
40
+ return "";
41
+ const budget = Math.max(MIN_MAX_LENGTH, Math.floor(maxLength));
42
+ if (name.length <= budget)
43
+ return name;
44
+ const lastDot = name.lastIndexOf(".");
45
+ const extensionLength = name.length - lastDot;
46
+ const hasExtension = lastDot > 0 &&
47
+ lastDot < name.length - 1 &&
48
+ extensionLength <= MAX_EXTENSION_LENGTH;
49
+ if (!hasExtension) {
50
+ return name.slice(0, budget - 1) + ELLIPSIS;
51
+ }
52
+ const extension = name.slice(lastDot);
53
+ const stemBudget = budget - extension.length - 1;
54
+ if (stemBudget <= 0) {
55
+ return name.slice(0, budget - 1) + ELLIPSIS;
56
+ }
57
+ return name.slice(0, stemBudget) + ELLIPSIS + extension;
58
+ }
@@ -0,0 +1,40 @@
1
+ import { truncateFileNameForLine } from "./truncateFileNameForLine";
2
+ describe("truncateFileNameForLine", () => {
3
+ it("returns an empty string when the input is undefined", () => {
4
+ expect(truncateFileNameForLine(undefined)).toBe("");
5
+ });
6
+ it("returns the name unchanged when it fits inside the budget", () => {
7
+ expect(truncateFileNameForLine("report.pdf")).toBe("report.pdf");
8
+ });
9
+ it("preserves a short extension and truncates the stem with a middle ellipsis", () => {
10
+ expect(truncateFileNameForLine("file-sample_2_dx.docx")).toBe("file-samp….docx");
11
+ });
12
+ it("preserves an uncommon extension like .fodt", () => {
13
+ expect(truncateFileNameForLine("very-long-name-for-document.fodt")).toBe("very-long….fodt");
14
+ });
15
+ it("preserves longer extensions like .numbers", () => {
16
+ expect(truncateFileNameForLine("quarterly-budget.numbers")).toBe("quarte….numbers");
17
+ });
18
+ it("treats overly long trailing tokens as not-an-extension and tail truncates", () => {
19
+ expect(truncateFileNameForLine("file.verylongextensionname")).toBe("file.verylonge…");
20
+ });
21
+ it("truncates a URL-shaped filename with no detectable extension using a tail ellipsis", () => {
22
+ expect(truncateFileNameForLine("vertopal.com/converter")).toBe("vertopal.com/c…");
23
+ });
24
+ it("falls back to tail truncation when the extension cannot fit", () => {
25
+ const long = "a".repeat(50) + ".verylongextension";
26
+ expect(truncateFileNameForLine(long, 24)).toMatch(/^a+…$/);
27
+ });
28
+ it("respects a caller-supplied maxLength", () => {
29
+ expect(truncateFileNameForLine("report.pdf", 6)).toBe("r….pdf");
30
+ });
31
+ it("returns a name shorter than the budget unchanged regardless of extension", () => {
32
+ expect(truncateFileNameForLine("notes.txt")).toBe("notes.txt");
33
+ expect(truncateFileNameForLine("a/b/c")).toBe("a/b/c");
34
+ });
35
+ it("clamps maxLength to a sensible minimum to preserve the single-line guarantee", () => {
36
+ expect(truncateFileNameForLine("report.pdf", 0)).toBe("r…");
37
+ expect(truncateFileNameForLine("report.pdf", -5)).toBe("r…");
38
+ expect(truncateFileNameForLine("report.pdf", 1)).toBe("r…");
39
+ });
40
+ });