@freelygive/canvas-utils 0.2.0 → 0.3.2-dev.b3dd8a2a

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
@@ -7,11 +7,28 @@ Canvas utility components and test helpers for Drupal Canvas projects.
7
7
  Utility components are scaffolded into your project via
8
8
  [@freelygive/npm-scaffold](https://www.npmjs.com/package/@freelygive/npm-scaffold).
9
9
 
10
- | Component | Description |
11
- |---|---|
12
- | `utils_editor_note` | Editor note component and `isCanvasEditorMode()` detection |
13
- | `utils_slots` | `getSlotChildren()` for parsing Canvas slot content |
14
- | `utils_entity` | `useMainEntity()` hook for fetching the page's main entity |
10
+ | Component | Description |
11
+ | ------------------- | ---------------------------------------------------------- |
12
+ | `utils_editor_note` | Editor note component, `isCanvasEditorMode()` detection, and `useEditorFullHeight()` hook |
13
+ | `utils_slots` | `getSlotChildren()` for parsing Canvas slot content |
14
+ | `utils_entity` | `useMainEntity()` hook for fetching the page's main entity |
15
+
16
+ ### `useEditorFullHeight(enabled)`
17
+
18
+ Canvas editor mode renders components inside an iframe whose height is stretched
19
+ to fit content, which breaks `min-h-screen` / `h-screen` CSS. This hook reads
20
+ the actual browser viewport height and returns a `{ minHeight }` style object
21
+ set to 80% of the viewport. Returns `undefined` when not in editor mode, so CSS
22
+ handles it normally.
23
+
24
+ ```jsx
25
+ import { useEditorFullHeight, isCanvasEditorMode } from '@/components/utils_editor_note';
26
+
27
+ const MyComponent = () => {
28
+ const editorStyle = useEditorFullHeight(isCanvasEditorMode());
29
+ return <div className="min-h-screen" style={editorStyle}>...</div>;
30
+ };
31
+ ```
15
32
 
16
33
  ## Test Helpers
17
34
 
@@ -20,8 +37,8 @@ Built JS entry points for use in Storybook test stories.
20
37
  ### Simulating Canvas Slots
21
38
 
22
39
  Canvas delivers slot content as HTML containing `<canvas-island>` custom
23
- elements. In Storybook, use the canvas-slots helpers to replicate this format
24
- so components can be tested under realistic conditions.
40
+ elements. In Storybook, use the canvas-slots helpers to replicate this format so
41
+ components can be tested under realistic conditions.
25
42
 
26
43
  ```js
27
44
  import {
@@ -52,8 +69,8 @@ export const WithCanvasSlot: Story = {
52
69
  ### Simulating Editor Mode
53
70
 
54
71
  Canvas editor mode changes component behaviour (e.g. expanding all accordion
55
- items for editing). Use the `withEditorMode` decorator to enable editor mode
56
- for the lifetime of a story:
72
+ items for editing). Use the `withEditorMode` decorator to enable editor mode for
73
+ the lifetime of a story:
57
74
 
58
75
  ```js
59
76
  import { withEditorMode } from '@freelygive/canvas-utils/testing/editor-mode';
@@ -65,11 +82,11 @@ export const InEditorMode: Story = {
65
82
  ```
66
83
 
67
84
  The decorator enables editor mode before the first render and cleans up on
68
- unmount via `useEffect`, so state is restored even when tests are interrupted
69
- or Storybook navigates away.
85
+ unmount via `useEffect`, so state is restored even when tests are interrupted or
86
+ Storybook navigates away.
70
87
 
71
- `enableEditorMode()` and `disableEditorMode()` are also exported for cases
72
- that need direct control.
88
+ `enableEditorMode()` and `disableEditorMode()` are also exported for cases that
89
+ need direct control.
73
90
 
74
91
  ## Setup
75
92
 
@@ -97,16 +114,16 @@ Running `npm install` will scaffold the utility components into
97
114
 
98
115
  ### canvas-slots
99
116
 
100
- | Export | Description |
101
- |---|---|
102
- | `rawProp(value)` | Wraps a value in the `["raw", value]` tuple format used by Canvas island props |
103
- | `createCanvasIsland(props)` | Creates a `<canvas-island>` HTML string from a props object |
104
- | `createCanvasSlot(html)` | Creates a React element simulating a Canvas slot for `getSlotChildren()` to parse |
117
+ | Export | Description |
118
+ | --------------------------- | --------------------------------------------------------------------------------- |
119
+ | `rawProp(value)` | Wraps a value in the `["raw", value]` tuple format used by Canvas island props |
120
+ | `createCanvasIsland(props)` | Creates a `<canvas-island>` HTML string from a props object |
121
+ | `createCanvasSlot(html)` | Creates a React element simulating a Canvas slot for `getSlotChildren()` to parse |
105
122
 
106
123
  ### editor-mode
107
124
 
108
- | Export | Description |
109
- |---|---|
110
- | `withEditorMode` | Storybook decorator — enables editor mode before render, cleans up on unmount |
111
- | `enableEditorMode()` | Sets `window.drupalSettings.canvas` to simulate Canvas editor mode |
112
- | `disableEditorMode()` | Removes `window.drupalSettings.canvas` |
125
+ | Export | Description |
126
+ | --------------------- | ----------------------------------------------------------------------------- |
127
+ | `withEditorMode` | Storybook decorator — enables editor mode before render, cleans up on unmount |
128
+ | `enableEditorMode()` | Sets `window.drupalSettings.canvas` to simulate Canvas editor mode |
129
+ | `disableEditorMode()` | Removes `window.drupalSettings.canvas` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@freelygive/canvas-utils",
3
- "version": "0.2.0",
3
+ "version": "0.3.2-dev.b3dd8a2a",
4
4
  "description": "Canvas utility components and test helpers for Drupal Canvas projects.",
5
5
  "type": "module",
6
6
  "files": [
@@ -40,5 +40,9 @@
40
40
  "tsup": "^8.0.0",
41
41
  "typescript": "^5.0.0"
42
42
  },
43
- "license": "GPL-2.0-or-later"
43
+ "license": "GPL-2.0-or-later",
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "https://gitlab.com/freelygive/canvas-utils"
47
+ }
44
48
  }
@@ -1,3 +1,5 @@
1
+ import { useEffect, useState } from 'react';
2
+
1
3
  /**
2
4
  * Detect if we're in Canvas page editor mode
3
5
  * Returns true when the component is being edited in the Canvas UI
@@ -24,6 +26,53 @@ export const isCanvasEditorMode = () => {
24
26
  return false;
25
27
  };
26
28
 
29
+ /**
30
+ * Get the actual browser viewport height, even inside Canvas editor iframes.
31
+ * Tries window.top (same-origin), then visualViewport, then falls back to
32
+ * a capped window.innerHeight (max 1200px to guard against stretched iframes).
33
+ */
34
+ const getViewportHeight = () => {
35
+ try {
36
+ if (window.top && window.top.innerHeight > 0) {
37
+ return window.top.innerHeight;
38
+ }
39
+ } catch {
40
+ // Cross-origin — fall through
41
+ }
42
+ if (window.visualViewport?.height > 0) {
43
+ return window.visualViewport.height;
44
+ }
45
+ return Math.min(window.innerHeight, 1200);
46
+ };
47
+
48
+ /**
49
+ * Hook that returns a minHeight style for full-height sections in editor mode.
50
+ * Canvas editor mode breaks min-h-screen / h-screen because the component
51
+ * renders inside an iframe whose height is stretched to fit content.
52
+ * We read the actual browser viewport height and apply 80% as inline style.
53
+ * Returns undefined when not in editor mode (let CSS handle it).
54
+ */
55
+ export const useEditorFullHeight = (enabled) => {
56
+ const [minHeight, setMinHeight] = useState(undefined);
57
+
58
+ useEffect(() => {
59
+ if (!enabled) return;
60
+ const update = () =>
61
+ setMinHeight(`${Math.round(getViewportHeight() * 0.8)}px`);
62
+ update();
63
+ // Listen on visualViewport (preferred) and window resize as fallback
64
+ const vv = window.visualViewport;
65
+ if (vv) vv.addEventListener('resize', update);
66
+ window.addEventListener('resize', update);
67
+ return () => {
68
+ if (vv) vv.removeEventListener('resize', update);
69
+ window.removeEventListener('resize', update);
70
+ };
71
+ }, [enabled]);
72
+
73
+ return enabled ? { minHeight } : undefined;
74
+ };
75
+
27
76
  /**
28
77
  * Editor note component for displaying messages only visible in Canvas editor mode
29
78
  * Used to explain runtime behavior that differs from editor preview