@furystack/shades-common-components 13.3.1 โ†’ 13.4.1

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 (54) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/esm/components/alert.d.ts.map +1 -1
  3. package/esm/components/alert.js +7 -6
  4. package/esm/components/alert.js.map +1 -1
  5. package/esm/components/cache-view.d.ts +2 -1
  6. package/esm/components/cache-view.d.ts.map +1 -1
  7. package/esm/components/cache-view.js +9 -7
  8. package/esm/components/cache-view.js.map +1 -1
  9. package/esm/components/cache-view.spec.js +195 -145
  10. package/esm/components/cache-view.spec.js.map +1 -1
  11. package/esm/components/command-palette/command-palette-suggestion-list.js +1 -1
  12. package/esm/components/command-palette/command-palette-suggestion-list.js.map +1 -1
  13. package/esm/components/inputs/input.d.ts.map +1 -1
  14. package/esm/components/inputs/input.js +2 -0
  15. package/esm/components/inputs/input.js.map +1 -1
  16. package/esm/components/inputs/select.d.ts.map +1 -1
  17. package/esm/components/inputs/select.js +3 -1
  18. package/esm/components/inputs/select.js.map +1 -1
  19. package/esm/components/result.d.ts +4 -2
  20. package/esm/components/result.d.ts.map +1 -1
  21. package/esm/components/result.js +11 -10
  22. package/esm/components/result.js.map +1 -1
  23. package/esm/components/suggest/index.d.ts.map +1 -1
  24. package/esm/components/suggest/index.js +7 -3
  25. package/esm/components/suggest/index.js.map +1 -1
  26. package/esm/components/tabs.d.ts +2 -0
  27. package/esm/components/tabs.d.ts.map +1 -1
  28. package/esm/components/tabs.js +5 -4
  29. package/esm/components/tabs.js.map +1 -1
  30. package/esm/components/tabs.spec.js +57 -0
  31. package/esm/components/tabs.spec.js.map +1 -1
  32. package/esm/components/tree/tree.d.ts.map +1 -1
  33. package/esm/components/tree/tree.js +1 -0
  34. package/esm/components/tree/tree.js.map +1 -1
  35. package/esm/components/wizard/index.d.ts +2 -1
  36. package/esm/components/wizard/index.d.ts.map +1 -1
  37. package/esm/components/wizard/index.js +3 -3
  38. package/esm/components/wizard/index.js.map +1 -1
  39. package/esm/components/wizard/index.spec.js +46 -1
  40. package/esm/components/wizard/index.spec.js.map +1 -1
  41. package/package.json +6 -6
  42. package/src/components/alert.tsx +9 -6
  43. package/src/components/cache-view.spec.tsx +266 -173
  44. package/src/components/cache-view.tsx +21 -8
  45. package/src/components/command-palette/command-palette-suggestion-list.tsx +1 -1
  46. package/src/components/inputs/input.tsx +2 -0
  47. package/src/components/inputs/select.tsx +3 -1
  48. package/src/components/result.tsx +17 -10
  49. package/src/components/suggest/index.tsx +18 -15
  50. package/src/components/tabs.spec.tsx +72 -0
  51. package/src/components/tabs.tsx +9 -4
  52. package/src/components/tree/tree.tsx +1 -0
  53. package/src/components/wizard/index.spec.tsx +57 -1
  54. package/src/components/wizard/index.tsx +5 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # Changelog
2
2
 
3
+ ## [13.4.1] - 2026-03-06
4
+
5
+ ### ๐Ÿ› Bug Fixes
6
+
7
+ - Fixed duplicated content on re-render in `Alert` and `Result` components caused by reusing module-level JSX element constants across renders. Default icons are now created via factory functions (`getDefaultIcon()`) that return fresh elements per render call.
8
+ - Updated `Result` exports: renamed `resultDefaultIcons` to `resultGetDefaultIcon` (factory function) and added `resultDefaultIconDefs` (icon definition map)
9
+
10
+ ### ๐Ÿงช Tests
11
+
12
+ - Refactored `CacheView` tests to use `using()` / `usingAsync()` wrappers for `Cache` disposal, ensuring proper cleanup even if assertions fail
13
+
14
+ ### ๐Ÿ”ง Chores
15
+
16
+ - Added `eslint-disable` comments for intentional `no-css-state-hooks` usage in `Input` (focused state) and `Select` (isFocused state) components where CSS state management via `useState` is the intended pattern
17
+
18
+ ## [13.4.0] - 2026-03-05
19
+
20
+ ### โœจ Features
21
+
22
+ - Added `viewTransition` prop to `Tabs` โ€” animates tab panel switches via the View Transition API when the active tab changes
23
+ - Added `viewTransition` prop to `Wizard` โ€” animates step transitions (next/prev) via the View Transition API
24
+ - Added `viewTransition` prop to `CacheView` โ€” animates state category changes (loading โ†’ value, value โ†’ error, etc.) via the View Transition API
25
+
26
+ ### ๐Ÿงช Tests
27
+
28
+ - Added tests for `Tabs` verifying `startViewTransition` is called on tab switch when enabled and skipped when not set
29
+ - Added tests for `Wizard` verifying `startViewTransition` is called on next/prev navigation when enabled and skipped when not set
30
+ - Added tests for `CacheView` verifying `startViewTransition` is called on state category changes when enabled and skipped when not set
31
+
32
+ ### โฌ†๏ธ Dependencies
33
+
34
+ - Updated `@furystack/shades` peer dependency
35
+
3
36
  ## [13.3.1] - 2026-03-04
4
37
 
5
38
  ### โฌ†๏ธ Dependencies
@@ -1 +1 @@
1
- {"version":3,"file":"alert.d.ts","sourceRoot":"","sources":["../../src/components/alert.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAavD,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAA;AAEpE,MAAM,MAAM,UAAU,GAAG,cAAc,CAAC,WAAW,CAAC,GAAG;IACrD,QAAQ,CAAC,EAAE,aAAa,CAAA;IACxB,OAAO,CAAC,EAAE,QAAQ,GAAG,UAAU,GAAG,UAAU,CAAA;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,UAAU,KAAK,IAAI,CAAA;IAClC,IAAI,CAAC,EAAE,GAAG,CAAC,OAAO,GAAG,MAAM,CAAA;CAC5B,CAAA;AAgBD,eAAO,MAAM,KAAK;;;;;eArBL,aAAa;cACd,QAAQ,GAAG,UAAU,GAAG,UAAU;YACpC,MAAM;cACJ,CAAC,EAAE,EAAE,UAAU,KAAK,IAAI;WAC3B,GAAG,CAAC,OAAO,GAAG,MAAM;sEAqK3B,CAAA"}
1
+ {"version":3,"file":"alert.d.ts","sourceRoot":"","sources":["../../src/components/alert.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAavD,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAA;AAEpE,MAAM,MAAM,UAAU,GAAG,cAAc,CAAC,WAAW,CAAC,GAAG;IACrD,QAAQ,CAAC,EAAE,aAAa,CAAA;IACxB,OAAO,CAAC,EAAE,QAAQ,GAAG,UAAU,GAAG,UAAU,CAAA;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,UAAU,KAAK,IAAI,CAAA;IAClC,IAAI,CAAC,EAAE,GAAG,CAAC,OAAO,GAAG,MAAM,CAAA;CAC5B,CAAA;AAmBD,eAAO,MAAM,KAAK;;;;;eAxBL,aAAa;cACd,QAAQ,GAAG,UAAU,GAAG,UAAU;YACpC,MAAM;cACJ,CAAC,EAAE,EAAE,UAAU,KAAK,IAAI;WAC3B,GAAG,CAAC,OAAO,GAAG,MAAM;sEAwK3B,CAAA"}
@@ -9,12 +9,13 @@ const severityColorMap = {
9
9
  info: { ...paletteMainColors.info, light: cssVariableTheme.palette.info.light },
10
10
  success: { ...paletteMainColors.success, light: cssVariableTheme.palette.success.light },
11
11
  };
12
- const defaultIcons = {
13
- error: (createComponent(Icon, { icon: errorCircle, size: "small" })),
14
- warning: (createComponent(Icon, { icon: warningIcon, size: "small" })),
15
- info: (createComponent(Icon, { icon: infoIcon, size: "small" })),
16
- success: (createComponent(Icon, { icon: checkCircle, size: "small" })),
12
+ const defaultIconDefs = {
13
+ error: errorCircle,
14
+ warning: warningIcon,
15
+ info: infoIcon,
16
+ success: checkCircle,
17
17
  };
18
+ const getDefaultIcon = (severity) => (createComponent(Icon, { icon: defaultIconDefs[severity], size: "small" }));
18
19
  export const Alert = Shade({
19
20
  shadowDomName: 'shade-alert',
20
21
  css: {
@@ -115,7 +116,7 @@ export const Alert = Shade({
115
116
  ...style,
116
117
  },
117
118
  });
118
- const displayIcon = icon ?? defaultIcons[severity];
119
+ const displayIcon = icon ?? getDefaultIcon(severity);
119
120
  return (createComponent(createComponent, null,
120
121
  createComponent("span", { className: "alert-icon" }, displayIcon),
121
122
  createComponent("div", { className: "alert-content" },
@@ -1 +1 @@
1
- {"version":3,"file":"alert.js","sourceRoot":"","sources":["../../src/components/alert.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"alert.js","sourceRoot":"","sources":["../../src/components/alert.tsx"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import type { Cache, CacheWithValue } from '@furystack/cache';
2
- import type { PartialElement, ShadeComponent } from '@furystack/shades';
2
+ import type { PartialElement, ShadeComponent, ViewTransitionConfig } from '@furystack/shades';
3
3
  /**
4
4
  * Props for the CacheView component.
5
5
  * @typeParam TData - The type of data stored in the cache
@@ -25,6 +25,7 @@ export type CacheViewProps<TData, TArgs extends any[], TContentProps extends {
25
25
  * If not provided, a default Result + retry Button is shown.
26
26
  */
27
27
  error?: (error: unknown, retry: () => void) => JSX.Element;
28
+ viewTransition?: boolean | ViewTransitionConfig;
28
29
  } & (keyof Omit<TContentProps, 'data' | keyof PartialElement<HTMLElement>> extends never ? {
29
30
  contentProps?: never;
30
31
  } : {
@@ -1 +1 @@
1
- {"version":3,"file":"cache-view.d.ts","sourceRoot":"","sources":["../../src/components/cache-view.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAE7D,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAOvE;;;;;GAKG;AACH,MAAM,MAAM,cAAc,CACxB,KAAK,EACL,KAAK,SAAS,GAAG,EAAE,EACnB,aAAa,SAAS;IAAE,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC,CAAA;CAAE,IACrF;IACF,gDAAgD;IAChD,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;IAC1B,6DAA6D;IAC7D,IAAI,EAAE,KAAK,CAAA;IACX,gFAAgF;IAChF,OAAO,EAAE,cAAc,CAAC,aAAa,CAAC,CAAA;IACtC,kFAAkF;IAClF,MAAM,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACpB;;;;OAIG;IACH,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,IAAI,KAAK,GAAG,CAAC,OAAO,CAAA;CAC3D,GAAG,CAAC,MAAM,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,cAAc,CAAC,WAAW,CAAC,CAAC,SAAS,KAAK,GACpF;IAAE,YAAY,CAAC,EAAE,KAAK,CAAA;CAAE,GACxB;IAAE,YAAY,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,cAAc,CAAC,WAAW,CAAC,CAAC,CAAA;CAAE,CAAC,CAAA;AAgDtF,eAAO,MAAM,SAAS,EAgDL,CACf,KAAK,EACL,KAAK,SAAS,GAAG,EAAE,EACnB,aAAa,SAAS;IAAE,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC,CAAA;CAAE,EAEvF,KAAK,EAAE,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,aAAa,CAAC,KAC/C,GAAG,CAAC,OAAO,CAAA"}
1
+ {"version":3,"file":"cache-view.d.ts","sourceRoot":"","sources":["../../src/components/cache-view.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAE7D,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAO7F;;;;;GAKG;AACH,MAAM,MAAM,cAAc,CACxB,KAAK,EACL,KAAK,SAAS,GAAG,EAAE,EACnB,aAAa,SAAS;IAAE,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC,CAAA;CAAE,IACrF;IACF,gDAAgD;IAChD,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;IAC1B,6DAA6D;IAC7D,IAAI,EAAE,KAAK,CAAA;IACX,gFAAgF;IAChF,OAAO,EAAE,cAAc,CAAC,aAAa,CAAC,CAAA;IACtC,kFAAkF;IAClF,MAAM,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACpB;;;;OAIG;IACH,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,IAAI,KAAK,GAAG,CAAC,OAAO,CAAA;IAC1D,cAAc,CAAC,EAAE,OAAO,GAAG,oBAAoB,CAAA;CAChD,GAAG,CAAC,MAAM,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,cAAc,CAAC,WAAW,CAAC,CAAC,SAAS,KAAK,GACpF;IAAE,YAAY,CAAC,EAAE,KAAK,CAAA;CAAE,GACxB;IAAE,YAAY,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,cAAc,CAAC,WAAW,CAAC,CAAC,CAAA;CAAE,CAAC,CAAA;AAiDtF,eAAO,MAAM,SAAS,EA2DL,CACf,KAAK,EACL,KAAK,SAAS,GAAG,EAAE,EACnB,aAAa,SAAS;IAAE,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC,CAAA;CAAE,EAEvF,KAAK,EAAE,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,aAAa,CAAC,KAC/C,GAAG,CAAC,OAAO,CAAA"}
@@ -1,5 +1,5 @@
1
1
  import { hasCacheValue, isFailedCacheResult, isObsoleteCacheResult } from '@furystack/cache';
2
- import { Shade, createComponent } from '@furystack/shades';
2
+ import { Shade, createComponent, transitionedValue } from '@furystack/shades';
3
3
  import { cssVariableTheme } from '../services/css-variable-theme.js';
4
4
  import { Button } from './button.js';
5
5
  import { Result } from './result.js';
@@ -11,10 +11,12 @@ export const CacheView = Shade({
11
11
  fontFamily: cssVariableTheme.typography.fontFamily,
12
12
  },
13
13
  render: ({ props, useObservable, useState }) => {
14
- const { cache, args, content, loader, error, contentProps } = props;
14
+ const { cache, args, content, loader, error, contentProps, viewTransition } = props;
15
15
  const argsKey = JSON.stringify(args);
16
16
  const observable = cache.getObservable(...args);
17
17
  const [result] = useObservable(`cache-${argsKey}`, observable);
18
+ const getCategory = (r) => isFailedCacheResult(r) ? 'error' : hasCacheValue(r) ? 'value' : 'loading';
19
+ const displayedResult = transitionedValue(useState, 'displayedResult', result, viewTransition, (prev, next) => getCategory(prev) !== getCategory(next));
18
20
  const [lastReloadedArgsKey, setLastReloadedArgsKey] = useState('lastReloadedArgsKey', null);
19
21
  const retry = () => {
20
22
  cache.reload(...args).catch(() => {
@@ -22,13 +24,13 @@ export const CacheView = Shade({
22
24
  });
23
25
  };
24
26
  // 1. Error first
25
- if (isFailedCacheResult(result)) {
27
+ if (isFailedCacheResult(displayedResult)) {
26
28
  const errorRenderer = error ?? getDefaultErrorUi;
27
- return errorRenderer(result.error, retry);
29
+ return errorRenderer(displayedResult.error, retry);
28
30
  }
29
31
  // 2. Value next
30
- if (hasCacheValue(result)) {
31
- if (isObsoleteCacheResult(result)) {
32
+ if (hasCacheValue(displayedResult)) {
33
+ if (isObsoleteCacheResult(displayedResult)) {
32
34
  if (lastReloadedArgsKey !== argsKey) {
33
35
  setLastReloadedArgsKey(argsKey);
34
36
  cache.reload(...args).catch(() => {
@@ -40,7 +42,7 @@ export const CacheView = Shade({
40
42
  setLastReloadedArgsKey(null);
41
43
  }
42
44
  return createComponent(content, {
43
- data: result,
45
+ data: displayedResult,
44
46
  ...(contentProps ?? {}),
45
47
  });
46
48
  }
@@ -1 +1 @@
1
- {"version":3,"file":"cache-view.js","sourceRoot":"","sources":["../../src/components/cache-view.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AAE5F,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAE1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAA;AACpE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AA+BpC,MAAM,iBAAiB,GAAG,CAAC,KAAc,EAAE,KAAiB,EAAe,EAAE,CAC3E,CACE,gBAAC,MAAM,IAAC,MAAM,EAAC,OAAO,EAAC,KAAK,EAAC,sBAAsB,EAAC,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC;IACzE,gBAAC,MAAM,IAAC,OAAO,EAAC,UAAU,EAAC,OAAO,EAAE,KAAK,YAEhC,CACF,CACgB,CAAA;AAuC7B,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,CAAyB;IACrD,aAAa,EAAE,kBAAkB;IACjC,GAAG,EAAE;QACH,UAAU,EAAE,gBAAgB,CAAC,UAAU,CAAC,UAAU;KACnD;IACD,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,EAAsB,EAAE;QACjE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,KAAK,CAAA;QAEnE,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;QACpC,MAAM,UAAU,GAAG,KAAK,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC,CAAA;QAE/C,MAAM,CAAC,MAAM,CAAC,GAAG,aAAa,CAAC,SAAS,OAAO,EAAE,EAAE,UAAU,CAAC,CAAA;QAE9D,MAAM,CAAC,mBAAmB,EAAE,sBAAsB,CAAC,GAAG,QAAQ,CAAgB,qBAAqB,EAAE,IAAI,CAAC,CAAA;QAE1G,MAAM,KAAK,GAAG,GAAG,EAAE;YACjB,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;gBAC/B,sCAAsC;YACxC,CAAC,CAAC,CAAA;QACJ,CAAC,CAAA;QAED,iBAAiB;QACjB,IAAI,mBAAmB,CAAC,MAAM,CAAC,EAAE,CAAC;YAChC,MAAM,aAAa,GAAG,KAAK,IAAI,iBAAiB,CAAA;YAChD,OAAO,aAAa,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;QAC3C,CAAC;QAED,gBAAgB;QAChB,IAAI,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1B,IAAI,qBAAqB,CAAC,MAAM,CAAC,EAAE,CAAC;gBAClC,IAAI,mBAAmB,KAAK,OAAO,EAAE,CAAC;oBACpC,sBAAsB,CAAC,OAAO,CAAC,CAAA;oBAC/B,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;wBAC/B,sCAAsC;oBACxC,CAAC,CAAC,CAAA;gBACJ,CAAC;YACH,CAAC;iBAAM,IAAI,mBAAmB,KAAK,IAAI,EAAE,CAAC;gBACxC,sBAAsB,CAAC,IAAI,CAAC,CAAA;YAC9B,CAAC;YACD,OAAO,eAAe,CAAC,OAAO,EAAE;gBAC9B,IAAI,EAAE,MAAM;gBACZ,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC;aACxB,CAA2B,CAAA;QAC9B,CAAC;QAED,kBAAkB;QAClB,OAAO,MAAM,IAAI,IAAI,CAAA;IACvB,CAAC;CACF,CAMe,CAAA"}
1
+ {"version":3,"file":"cache-view.js","sourceRoot":"","sources":["../../src/components/cache-view.tsx"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { Cache } from '@furystack/cache';
2
2
  import { Shade, createComponent, flushUpdates } from '@furystack/shades';
3
+ import { using, usingAsync } from '@furystack/utils';
3
4
  import { describe, expect, it, vi } from 'vitest';
4
5
  import { CacheView } from './cache-view.js';
5
6
  const TestContent = Shade({
@@ -27,202 +28,251 @@ describe('CacheView', () => {
27
28
  expect(typeof CacheView).toBe('function');
28
29
  });
29
30
  it('should create a cache-view element', () => {
30
- const cache = new Cache({ load: async (key) => key });
31
- const el = (createComponent(CacheView, { cache: cache, args: ['test'], content: TestContent }));
32
- expect(el).toBeDefined();
33
- expect(el.tagName?.toLowerCase()).toBe('shade-cache-view');
34
- cache[Symbol.dispose]();
31
+ using(new Cache({ load: async (key) => key }), (cache) => {
32
+ const el = (createComponent(CacheView, { cache: cache, args: ['test'], content: TestContent }));
33
+ expect(el).toBeDefined();
34
+ expect(el.tagName?.toLowerCase()).toBe('shade-cache-view');
35
+ });
35
36
  });
36
37
  describe('loading state', () => {
37
38
  it('should render null by default when loading', async () => {
38
- const cache = new Cache({
39
+ await usingAsync(new Cache({
39
40
  load: () => new Promise(() => { }),
41
+ }), async (cache) => {
42
+ const { cacheView } = await renderCacheView(cache, ['test']);
43
+ expect(cacheView.querySelector('test-cache-content')).toBeNull();
44
+ expect(cacheView.querySelector('shade-result')).toBeNull();
40
45
  });
41
- const { cacheView } = await renderCacheView(cache, ['test']);
42
- expect(cacheView.querySelector('test-cache-content')).toBeNull();
43
- expect(cacheView.querySelector('shade-result')).toBeNull();
44
- cache[Symbol.dispose]();
45
46
  });
46
47
  it('should render custom loader when provided', async () => {
47
- const cache = new Cache({
48
+ await usingAsync(new Cache({
48
49
  load: () => new Promise(() => { }),
50
+ }), async (cache) => {
51
+ const { cacheView } = await renderCacheView(cache, ['test'], {
52
+ loader: (createComponent("span", { className: "custom-loader" }, "Loading...")),
53
+ });
54
+ const loader = cacheView.querySelector('.custom-loader');
55
+ expect(loader).not.toBeNull();
56
+ expect(loader?.textContent).toBe('Loading...');
49
57
  });
50
- const { cacheView } = await renderCacheView(cache, ['test'], {
51
- loader: (createComponent("span", { className: "custom-loader" }, "Loading...")),
52
- });
53
- const loader = cacheView.querySelector('.custom-loader');
54
- expect(loader).not.toBeNull();
55
- expect(loader?.textContent).toBe('Loading...');
56
- cache[Symbol.dispose]();
57
58
  });
58
59
  });
59
60
  describe('loaded state', () => {
60
61
  it('should render content when cache has loaded value', async () => {
61
- const cache = new Cache({ load: async (key) => `Hello ${key}` });
62
- await cache.get('world');
63
- const { cacheView } = await renderCacheView(cache, ['world']);
64
- const contentEl = cacheView.querySelector('test-cache-content');
65
- expect(contentEl).not.toBeNull();
66
- const contentComponent = contentEl;
67
- contentComponent.updateComponent();
68
- await flushUpdates();
69
- const valueEl = contentComponent.querySelector('.content-value');
70
- expect(valueEl?.textContent).toBe('Hello world');
71
- cache[Symbol.dispose]();
62
+ await usingAsync(new Cache({ load: async (key) => `Hello ${key}` }), async (cache) => {
63
+ await cache.get('world');
64
+ const { cacheView } = await renderCacheView(cache, ['world']);
65
+ const contentEl = cacheView.querySelector('test-cache-content');
66
+ expect(contentEl).not.toBeNull();
67
+ const contentComponent = contentEl;
68
+ contentComponent.updateComponent();
69
+ await flushUpdates();
70
+ const valueEl = contentComponent.querySelector('.content-value');
71
+ expect(valueEl?.textContent).toBe('Hello world');
72
+ });
72
73
  });
73
74
  });
74
75
  describe('failed state', () => {
75
76
  it('should render default error UI when cache has failed', async () => {
76
- const cache = new Cache({
77
+ await usingAsync(new Cache({
77
78
  load: async () => {
78
79
  throw new Error('Test failure');
79
80
  },
81
+ }), async (cache) => {
82
+ try {
83
+ await cache.get('test');
84
+ }
85
+ catch {
86
+ // expected
87
+ }
88
+ const { cacheView } = await renderCacheView(cache, ['test']);
89
+ const resultEl = cacheView.querySelector('shade-result');
90
+ expect(resultEl).not.toBeNull();
91
+ const resultComponent = resultEl;
92
+ resultComponent.updateComponent();
93
+ await flushUpdates();
94
+ const titleEl = resultComponent.querySelector('.result-title');
95
+ titleEl.updateComponent();
96
+ await flushUpdates();
97
+ expect(titleEl?.textContent).toBe('Something went wrong');
80
98
  });
81
- try {
82
- await cache.get('test');
83
- }
84
- catch {
85
- // expected
86
- }
87
- const { cacheView } = await renderCacheView(cache, ['test']);
88
- const resultEl = cacheView.querySelector('shade-result');
89
- expect(resultEl).not.toBeNull();
90
- const resultComponent = resultEl;
91
- resultComponent.updateComponent();
92
- await flushUpdates();
93
- const titleEl = resultComponent.querySelector('.result-title');
94
- titleEl.updateComponent();
95
- await flushUpdates();
96
- expect(titleEl?.textContent).toBe('Something went wrong');
97
- cache[Symbol.dispose]();
98
99
  });
99
100
  it('should render custom error UI when error prop is provided', async () => {
100
- const cache = new Cache({
101
+ await usingAsync(new Cache({
101
102
  load: async () => {
102
103
  throw new Error('Custom failure');
103
104
  },
105
+ }), async (cache) => {
106
+ try {
107
+ await cache.get('test');
108
+ }
109
+ catch {
110
+ // expected
111
+ }
112
+ const customError = vi.fn((err, _retry) => (createComponent("span", { className: "custom-error" }, String(err))));
113
+ const { cacheView } = await renderCacheView(cache, ['test'], { error: customError });
114
+ expect(customError).toHaveBeenCalledOnce();
115
+ expect(customError.mock.calls[0][0]).toBeInstanceOf(Error);
116
+ const customErrorEl = cacheView.querySelector('.custom-error');
117
+ expect(customErrorEl).not.toBeNull();
104
118
  });
105
- try {
106
- await cache.get('test');
107
- }
108
- catch {
109
- // expected
110
- }
111
- const customError = vi.fn((err, _retry) => (createComponent("span", { className: "custom-error" }, String(err))));
112
- const { cacheView } = await renderCacheView(cache, ['test'], { error: customError });
113
- expect(customError).toHaveBeenCalledOnce();
114
- expect(customError.mock.calls[0][0]).toBeInstanceOf(Error);
115
- const customErrorEl = cacheView.querySelector('.custom-error');
116
- expect(customErrorEl).not.toBeNull();
117
- cache[Symbol.dispose]();
118
119
  });
119
120
  it('should not render content when failed even if stale value exists', async () => {
120
- const cache = new Cache({ load: async (key) => key });
121
- await cache.get('test');
122
- cache.setExplicitValue({
123
- loadArgs: ['test'],
124
- value: { status: 'failed', error: new Error('fail'), value: 'stale', updatedAt: new Date() },
121
+ await usingAsync(new Cache({ load: async (key) => key }), async (cache) => {
122
+ await cache.get('test');
123
+ cache.setExplicitValue({
124
+ loadArgs: ['test'],
125
+ value: { status: 'failed', error: new Error('fail'), value: 'stale', updatedAt: new Date() },
126
+ });
127
+ const { cacheView } = await renderCacheView(cache, ['test']);
128
+ expect(cacheView.querySelector('test-cache-content')).toBeNull();
129
+ expect(cacheView.querySelector('shade-result')).not.toBeNull();
125
130
  });
126
- const { cacheView } = await renderCacheView(cache, ['test']);
127
- expect(cacheView.querySelector('test-cache-content')).toBeNull();
128
- expect(cacheView.querySelector('shade-result')).not.toBeNull();
129
- cache[Symbol.dispose]();
130
131
  });
131
132
  it('should call cache.reload when retry is invoked', async () => {
132
133
  const loadFn = vi.fn(async () => {
133
134
  throw new Error('fail');
134
135
  });
135
- const cache = new Cache({ load: loadFn });
136
- try {
137
- await cache.get('test');
138
- }
139
- catch {
140
- // expected
141
- }
142
- let capturedRetry;
143
- const customError = (_err, retry) => {
144
- capturedRetry = retry;
145
- return (createComponent("span", { className: "custom-error" }, "Error"));
146
- };
147
- await renderCacheView(cache, ['test'], { error: customError });
148
- expect(capturedRetry).toBeDefined();
149
- loadFn.mockResolvedValueOnce('recovered');
150
- capturedRetry();
151
- await flushUpdates();
152
- const observable = cache.getObservable('test');
153
- const result = observable.getValue();
154
- expect(result.status).toBe('loaded');
155
- expect(result.value).toBe('recovered');
156
- cache[Symbol.dispose]();
136
+ await usingAsync(new Cache({ load: loadFn }), async (cache) => {
137
+ try {
138
+ await cache.get('test');
139
+ }
140
+ catch {
141
+ // expected
142
+ }
143
+ let capturedRetry;
144
+ const customError = (_err, retry) => {
145
+ capturedRetry = retry;
146
+ return (createComponent("span", { className: "custom-error" }, "Error"));
147
+ };
148
+ await renderCacheView(cache, ['test'], { error: customError });
149
+ expect(capturedRetry).toBeDefined();
150
+ loadFn.mockResolvedValueOnce('recovered');
151
+ capturedRetry();
152
+ await flushUpdates();
153
+ const observable = cache.getObservable('test');
154
+ const result = observable.getValue();
155
+ expect(result.status).toBe('loaded');
156
+ expect(result.value).toBe('recovered');
157
+ });
157
158
  });
158
159
  });
159
160
  describe('obsolete state', () => {
160
161
  it('should render content when obsolete and trigger reload', async () => {
161
162
  const loadFn = vi.fn(async (key) => `Hello ${key}`);
162
- const cache = new Cache({ load: loadFn });
163
- await cache.get('test');
164
- cache.setObsolete('test');
165
- const { cacheView } = await renderCacheView(cache, ['test']);
166
- const contentEl = cacheView.querySelector('test-cache-content');
167
- expect(contentEl).not.toBeNull();
168
- await flushUpdates();
169
- // reload should have been called (initial load + obsolete reload)
170
- expect(loadFn).toHaveBeenCalledTimes(2);
171
- cache[Symbol.dispose]();
163
+ await usingAsync(new Cache({ load: loadFn }), async (cache) => {
164
+ await cache.get('test');
165
+ cache.setObsolete('test');
166
+ const { cacheView } = await renderCacheView(cache, ['test']);
167
+ const contentEl = cacheView.querySelector('test-cache-content');
168
+ expect(contentEl).not.toBeNull();
169
+ await flushUpdates();
170
+ // reload should have been called (initial load + obsolete reload)
171
+ expect(loadFn).toHaveBeenCalledTimes(2);
172
+ });
172
173
  });
173
174
  });
174
175
  describe('error takes priority over value', () => {
175
176
  it('should show error when failed with value, not content', async () => {
176
- const cache = new Cache({ load: async (key) => key });
177
- await cache.get('test');
178
- const failedWithValue = {
179
- status: 'failed',
180
- error: new Error('whoops'),
181
- value: 'stale-data',
182
- updatedAt: new Date(),
183
- };
184
- cache.setExplicitValue({ loadArgs: ['test'], value: failedWithValue });
185
- const { cacheView } = await renderCacheView(cache, ['test']);
186
- expect(cacheView.querySelector('test-cache-content')).toBeNull();
187
- expect(cacheView.querySelector('shade-result')).not.toBeNull();
188
- cache[Symbol.dispose]();
177
+ await usingAsync(new Cache({ load: async (key) => key }), async (cache) => {
178
+ await cache.get('test');
179
+ const failedWithValue = {
180
+ status: 'failed',
181
+ error: new Error('whoops'),
182
+ value: 'stale-data',
183
+ updatedAt: new Date(),
184
+ };
185
+ cache.setExplicitValue({ loadArgs: ['test'], value: failedWithValue });
186
+ const { cacheView } = await renderCacheView(cache, ['test']);
187
+ expect(cacheView.querySelector('test-cache-content')).toBeNull();
188
+ expect(cacheView.querySelector('shade-result')).not.toBeNull();
189
+ });
189
190
  });
190
191
  });
191
192
  describe('contentProps', () => {
192
193
  it('should forward contentProps to the content component', async () => {
193
- const cache = new Cache({ load: async (key) => `Hello ${key}` });
194
- await cache.get('world');
195
- const el = (createComponent("div", null,
196
- createComponent(CacheView, { cache: cache, args: ['world'], content: TestContentWithLabel, contentProps: { label: 'Greeting' } })));
197
- const cacheView = el.firstElementChild;
198
- cacheView.updateComponent();
199
- await flushUpdates();
200
- const contentEl = cacheView.querySelector('test-cache-content-with-label');
201
- expect(contentEl).not.toBeNull();
202
- contentEl.updateComponent();
203
- await flushUpdates();
204
- const valueEl = contentEl.querySelector('.content-value');
205
- expect(valueEl?.textContent).toBe('Greeting: Hello world');
206
- cache[Symbol.dispose]();
194
+ await usingAsync(new Cache({ load: async (key) => `Hello ${key}` }), async (cache) => {
195
+ await cache.get('world');
196
+ const el = (createComponent("div", null,
197
+ createComponent(CacheView, { cache: cache, args: ['world'], content: TestContentWithLabel, contentProps: { label: 'Greeting' } })));
198
+ const cacheView = el.firstElementChild;
199
+ cacheView.updateComponent();
200
+ await flushUpdates();
201
+ const contentEl = cacheView.querySelector('test-cache-content-with-label');
202
+ expect(contentEl).not.toBeNull();
203
+ contentEl.updateComponent();
204
+ await flushUpdates();
205
+ const valueEl = contentEl.querySelector('.content-value');
206
+ expect(valueEl?.textContent).toBe('Greeting: Hello world');
207
+ });
207
208
  });
208
209
  it('should forward contentProps when cache entry is obsolete', async () => {
209
210
  const loadFn = vi.fn(async (key) => `Hello ${key}`);
210
- const cache = new Cache({ load: loadFn });
211
- await cache.get('world');
212
- cache.setObsolete('world');
213
- const el = (createComponent("div", null,
214
- createComponent(CacheView, { cache: cache, args: ['world'], content: TestContentWithLabel, contentProps: { label: 'Stale' } })));
215
- const cacheView = el.firstElementChild;
216
- cacheView.updateComponent();
217
- await flushUpdates();
218
- const contentEl = cacheView.querySelector('test-cache-content-with-label');
219
- expect(contentEl).not.toBeNull();
220
- contentEl.updateComponent();
221
- await flushUpdates();
222
- const valueEl = contentEl.querySelector('.content-value');
223
- expect(valueEl?.textContent).toBe('Stale: Hello world');
224
- expect(loadFn).toHaveBeenCalledTimes(2);
225
- cache[Symbol.dispose]();
211
+ await usingAsync(new Cache({ load: loadFn }), async (cache) => {
212
+ await cache.get('world');
213
+ cache.setObsolete('world');
214
+ const el = (createComponent("div", null,
215
+ createComponent(CacheView, { cache: cache, args: ['world'], content: TestContentWithLabel, contentProps: { label: 'Stale' } })));
216
+ const cacheView = el.firstElementChild;
217
+ cacheView.updateComponent();
218
+ await flushUpdates();
219
+ const contentEl = cacheView.querySelector('test-cache-content-with-label');
220
+ expect(contentEl).not.toBeNull();
221
+ contentEl.updateComponent();
222
+ await flushUpdates();
223
+ const valueEl = contentEl.querySelector('.content-value');
224
+ expect(valueEl?.textContent).toBe('Stale: Hello world');
225
+ expect(loadFn).toHaveBeenCalledTimes(2);
226
+ });
227
+ });
228
+ });
229
+ describe('view transitions', () => {
230
+ const mockStartViewTransition = () => {
231
+ const spy = vi.fn((optionsOrCallback) => {
232
+ const update = typeof optionsOrCallback === 'function' ? optionsOrCallback : optionsOrCallback.update;
233
+ update?.();
234
+ return {
235
+ finished: Promise.resolve(),
236
+ ready: Promise.resolve(),
237
+ updateCallbackDone: Promise.resolve(),
238
+ skipTransition: vi.fn(),
239
+ };
240
+ });
241
+ document.startViewTransition = spy;
242
+ return spy;
243
+ };
244
+ it('should call startViewTransition when viewTransition is enabled and cache state category changes', async () => {
245
+ const spy = mockStartViewTransition();
246
+ await usingAsync(new Cache({ load: async (key) => `loaded-${key}` }), async (cache) => {
247
+ const el = (createComponent("div", null,
248
+ createComponent(CacheView, { cache: cache, args: ['test'], content: TestContent, loader: createComponent("span", { className: "loader" }, "Loading"), viewTransition: true })));
249
+ const cacheView = el.firstElementChild;
250
+ cacheView.updateComponent();
251
+ await flushUpdates();
252
+ expect(cacheView.querySelector('.loader')).toBeTruthy();
253
+ spy.mockClear();
254
+ await cache.get('test');
255
+ cacheView.updateComponent();
256
+ await flushUpdates();
257
+ expect(spy).toHaveBeenCalled();
258
+ });
259
+ delete document.startViewTransition;
260
+ });
261
+ it('should not call startViewTransition when viewTransition is not set', async () => {
262
+ const spy = mockStartViewTransition();
263
+ await usingAsync(new Cache({ load: async (key) => `loaded-${key}` }), async (cache) => {
264
+ const el = (createComponent("div", null,
265
+ createComponent(CacheView, { cache: cache, args: ['test'], content: TestContent, loader: createComponent("span", { className: "loader" }, "Loading") })));
266
+ const cacheView = el.firstElementChild;
267
+ cacheView.updateComponent();
268
+ await flushUpdates();
269
+ spy.mockClear();
270
+ await cache.get('test');
271
+ cacheView.updateComponent();
272
+ await flushUpdates();
273
+ expect(spy).not.toHaveBeenCalled();
274
+ });
275
+ delete document.startViewTransition;
226
276
  });
227
277
  });
228
278
  });