weg-shared-layout 0.0.23 → 0.0.24
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/LICENSE +21 -21
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/weg-footer_2.cjs.entry.js +121 -58
- package/dist/cjs/weg-shared-layout.cjs.js +1 -1
- package/dist/collection/collection-manifest.json +0 -1
- package/dist/collection/components/weg-footer/icons/instagram.svg +5 -5
- package/dist/collection/components/weg-footer/icons/linkedin.svg +3 -3
- package/dist/collection/components/weg-footer/icons/tiktok.svg +3 -3
- package/dist/collection/components/weg-footer/icons/youtube.svg +4 -4
- package/dist/collection/components/weg-footer/weg-footer.css +218 -201
- package/dist/collection/components/weg-footer/weg-footer.js +2 -2
- package/dist/collection/components/weg-header/weg-header.css +473 -365
- package/dist/collection/components/weg-header/weg-header.js +114 -58
- package/dist/collection/constants/breakpoints.js +6 -0
- package/dist/collection/styles/shared.css +72 -78
- package/dist/components/weg-footer.js +1 -1
- package/dist/components/weg-header.js +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/esm/weg-footer_2.entry.js +121 -58
- package/dist/esm/weg-shared-layout.js +1 -1
- package/dist/types/components/weg-header/weg-header.d.ts +20 -7
- package/dist/types/components.d.ts +0 -42
- package/dist/types/constants/breakpoints.d.ts +6 -0
- package/dist/weg-shared-layout/p-c6ec3888.entry.js +1 -0
- package/dist/weg-shared-layout/weg-shared-layout.esm.js +1 -1
- package/docs/angular-integration-guide.md +1026 -0
- package/docs/angular.md +247 -138
- package/docs/nextjs.md +305 -305
- package/docs/publishing.md +48 -48
- package/docs/react.md +261 -261
- package/docs/vanilla.md +140 -140
- package/package.json +12 -8
- package/readme.md +108 -107
- package/src/assets/dummy-data.json +147 -147
- package/src/constants/breakpoints.ts +7 -0
- package/src/constants/weg-urls.ts +23 -23
- package/dist/cjs/my-component.cjs.entry.js +0 -33
- package/dist/collection/components/my-component/my-component.css +0 -3
- package/dist/collection/components/my-component/my-component.js +0 -95
- package/dist/components/my-component.d.ts +0 -11
- package/dist/components/my-component.js +0 -1
- package/dist/esm/my-component.entry.js +0 -31
- package/dist/types/components/my-component/my-component.d.ts +0 -16
- package/dist/weg-shared-layout/p-80fa529c.entry.js +0 -1
- package/dist/weg-shared-layout/p-d61033bd.entry.js +0 -1
- /package/dist/types/Users/{tan/Documents/code → rakin/Documents/WEG/repos}/weg-shared-layout/.stencil/test-utils/axe.d.ts +0 -0
- /package/dist/types/Users/{tan/Documents/code → rakin/Documents/WEG/repos}/weg-shared-layout/.stencil/test-utils/viewport.d.ts +0 -0
package/docs/publishing.md
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
# Publishing to npm
|
|
2
|
-
|
|
3
|
-
## `npm publish` does not run your build by default
|
|
4
|
-
|
|
5
|
-
Publishing packs **whatever is already on disk** under the paths listed in [`package.json` `files`](../package.json) (for this package: `dist/`, `loader/`, and `dummy-data.json`). It does **not** automatically run `npm run build` unless a lifecycle script does.
|
|
6
|
-
|
|
7
|
-
If you publish with an empty, missing, or half-built `dist/`, consumers get a broken tarball (missing `dist/esm/loader.js`, missing `dist/components/*.js`, etc.).
|
|
8
|
-
|
|
9
|
-
## What this repo does
|
|
10
|
-
|
|
11
|
-
The **`prepack`** script runs **`npm run build`** before every:
|
|
12
|
-
|
|
13
|
-
- `npm pack`
|
|
14
|
-
- `npm publish` (which packs first)
|
|
15
|
-
|
|
16
|
-
So the Stencil output and generated `loader/` are always fresh in the tarball.
|
|
17
|
-
|
|
18
|
-
```json
|
|
19
|
-
"scripts": {
|
|
20
|
-
"prepack": "npm run build",
|
|
21
|
-
"build": "stencil build"
|
|
22
|
-
}
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Release checklist
|
|
26
|
-
|
|
27
|
-
1. Bump **`version`** in `package.json`.
|
|
28
|
-
2. From the package root:
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
npm run build
|
|
32
|
-
npm test
|
|
33
|
-
npm pack --dry-run
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
Inspect the dry-run file list and spot-check for `dist/esm/loader.js`, `dist/components/weg-footer.js`, and `loader/index.js`.
|
|
37
|
-
|
|
38
|
-
3. Publish (runs `prepack` → build again):
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
npm publish
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
Use `npm publish --dry-run` if you want to exercise the full pack pipeline without uploading.
|
|
45
|
-
|
|
46
|
-
## CI
|
|
47
|
-
|
|
48
|
-
If you publish from CI, run **`npm run build`** (or rely on **`npm publish`**, which triggers **`prepack`**) in the same job before `npm publish`, and ensure **devDependencies** (e.g. `@stencil/core`) are installed so `stencil build` succeeds.
|
|
1
|
+
# Publishing to npm
|
|
2
|
+
|
|
3
|
+
## `npm publish` does not run your build by default
|
|
4
|
+
|
|
5
|
+
Publishing packs **whatever is already on disk** under the paths listed in [`package.json` `files`](../package.json) (for this package: `dist/`, `loader/`, and `dummy-data.json`). It does **not** automatically run `npm run build` unless a lifecycle script does.
|
|
6
|
+
|
|
7
|
+
If you publish with an empty, missing, or half-built `dist/`, consumers get a broken tarball (missing `dist/esm/loader.js`, missing `dist/components/*.js`, etc.).
|
|
8
|
+
|
|
9
|
+
## What this repo does
|
|
10
|
+
|
|
11
|
+
The **`prepack`** script runs **`npm run build`** before every:
|
|
12
|
+
|
|
13
|
+
- `npm pack`
|
|
14
|
+
- `npm publish` (which packs first)
|
|
15
|
+
|
|
16
|
+
So the Stencil output and generated `loader/` are always fresh in the tarball.
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
"scripts": {
|
|
20
|
+
"prepack": "npm run build",
|
|
21
|
+
"build": "stencil build"
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Release checklist
|
|
26
|
+
|
|
27
|
+
1. Bump **`version`** in `package.json`.
|
|
28
|
+
2. From the package root:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm run build
|
|
32
|
+
npm test
|
|
33
|
+
npm pack --dry-run
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Inspect the dry-run file list and spot-check for `dist/esm/loader.js`, `dist/components/weg-footer.js`, and `loader/index.js`.
|
|
37
|
+
|
|
38
|
+
3. Publish (runs `prepack` → build again):
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm publish
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Use `npm publish --dry-run` if you want to exercise the full pack pipeline without uploading.
|
|
45
|
+
|
|
46
|
+
## CI
|
|
47
|
+
|
|
48
|
+
If you publish from CI, run **`npm run build`** (or rely on **`npm publish`**, which triggers **`prepack`**) in the same job before `npm publish`, and ensure **devDependencies** (e.g. `@stencil/core`) are installed so `stencil build` succeeds.
|
package/docs/react.md
CHANGED
|
@@ -1,261 +1,261 @@
|
|
|
1
|
-
# React (SPA)
|
|
2
|
-
|
|
3
|
-
Guide for **client-rendered** React apps (Vite, Create React App, etc.). If you use **Next.js App Router**, see **[Next.js](./nextjs.md)** — SSR and Server Components require a different integration.
|
|
4
|
-
|
|
5
|
-
## Components
|
|
6
|
-
|
|
7
|
-
| Tag | Purpose |
|
|
8
|
-
| --- | --- |
|
|
9
|
-
| `<weg-header>` | Site header — bundled logo, CMS nav (signed out), built-in nav (signed in), Sign in / Manage Account / Sign out |
|
|
10
|
-
| `<weg-footer>` | Site footer — social links, columns, credits, copyright |
|
|
11
|
-
|
|
12
|
-
Both are **presentational** [Stencil](https://stenciljs.com/) Web Components. They **do not fetch data** — your app passes a **`layout`** payload (API, CMS, or [`dummy-data.json`](../src/assets/dummy-data.json)).
|
|
13
|
-
|
|
14
|
-
`<weg-header>` additionally accepts **`signed-in`**, **`user-name`**, and emits **`wegAuthClick`**.
|
|
15
|
-
|
|
16
|
-
## Requirements
|
|
17
|
-
|
|
18
|
-
| Requirement | Notes |
|
|
19
|
-
| --- | --- |
|
|
20
|
-
| **React 19+** | React 19 maps custom-element props to DOM **properties** when possible. Older React often sets props as string **attributes**, which breaks object payloads. |
|
|
21
|
-
| **Bundler** | Must resolve `node_modules` ESM/CJS from `weg-shared-layout`. |
|
|
22
|
-
| **TypeScript (optional)** | Module augmentation below; enable `resolveJsonModule` if you import `dummy-data.json`. |
|
|
23
|
-
|
|
24
|
-
## Install
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
npm i weg-shared-layout
|
|
28
|
-
# or
|
|
29
|
-
pnpm add weg-shared-layout
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## 1. Register custom elements (once)
|
|
33
|
-
|
|
34
|
-
Call `defineCustomElements()` **once** before the first render — typically in `main.tsx` / `index.tsx`:
|
|
35
|
-
|
|
36
|
-
```ts
|
|
37
|
-
import { defineCustomElements } from 'weg-shared-layout/loader';
|
|
38
|
-
|
|
39
|
-
defineCustomElements();
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Side-effect import the tags your app uses:
|
|
43
|
-
|
|
44
|
-
```ts
|
|
45
|
-
import 'weg-shared-layout/weg-header';
|
|
46
|
-
import 'weg-shared-layout/weg-footer';
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## 2. Layout shell
|
|
50
|
-
|
|
51
|
-
### Recommended: pass `layout` as an object (React 19+)
|
|
52
|
-
|
|
53
|
-
```tsx
|
|
54
|
-
import 'weg-shared-layout/weg-header';
|
|
55
|
-
import 'weg-shared-layout/weg-footer';
|
|
56
|
-
import layout from 'weg-shared-layout/dummy-data.json';
|
|
57
|
-
|
|
58
|
-
export function SiteLayout({ children }: { children: React.ReactNode }) {
|
|
59
|
-
return (
|
|
60
|
-
<>
|
|
61
|
-
<weg-header layout={layout} />
|
|
62
|
-
{children}
|
|
63
|
-
<weg-footer layout={layout} />
|
|
64
|
-
</>
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
### Fallback: `JSON.stringify` (React 18 or empty components)
|
|
70
|
-
|
|
71
|
-
```tsx
|
|
72
|
-
<weg-header layout={JSON.stringify(layout)} />
|
|
73
|
-
<weg-footer layout={JSON.stringify(layout)} />
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
## 3. Header auth
|
|
77
|
-
|
|
78
|
-
Define auth URLs once in your app:
|
|
79
|
-
|
|
80
|
-
```ts
|
|
81
|
-
// auth.ts
|
|
82
|
-
export const HEADER_SIGN_IN = {
|
|
83
|
-
label: 'Sign in',
|
|
84
|
-
href: 'https://account.warwickemploymentgroup.com/account/login',
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
export const ACCOUNT_LOGIN_HREF = HEADER_SIGN_IN.href;
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
### Signed out
|
|
91
|
-
|
|
92
|
-
Pass CMS/API layout with `dropdowns`, `links`, and `signIn`. Use [`dummy-data.json`](../src/assets/dummy-data.json) for the full shape. Keep auth URLs in a local file:
|
|
93
|
-
|
|
94
|
-
```ts
|
|
95
|
-
// auth.ts (host app)
|
|
96
|
-
export const HEADER_SIGN_IN = {
|
|
97
|
-
label: 'Sign in',
|
|
98
|
-
href: 'https://account.warwickemploymentgroup.com/account/login',
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
export const ACCOUNT_LOGIN_HREF = HEADER_SIGN_IN.href;
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
### Signed in
|
|
105
|
-
|
|
106
|
-
Set **`signed-in`** and optionally **`user-name`**. The component **ignores CMS nav** and shows built-in links: Find a job, Dashboard, Manage Account, Sign out.
|
|
107
|
-
|
|
108
|
-
```tsx
|
|
109
|
-
import { useCallback, useState } from 'react';
|
|
110
|
-
import { ACCOUNT_LOGIN_HREF, HEADER_SIGN_IN } from './auth';
|
|
111
|
-
import 'weg-shared-layout/weg-header';
|
|
112
|
-
import layout from 'weg-shared-layout/dummy-data.json';
|
|
113
|
-
|
|
114
|
-
export function SiteHeader() {
|
|
115
|
-
const [signedIn, setSignedIn] = useState(false);
|
|
116
|
-
const userName = signedIn ? 'Alex' : undefined;
|
|
117
|
-
|
|
118
|
-
const onAuthClick = useCallback((event: CustomEvent<{ action: 'sign-in' | 'sign-out' }>) => {
|
|
119
|
-
event.preventDefault();
|
|
120
|
-
|
|
121
|
-
if (event.detail.action === 'sign-out') {
|
|
122
|
-
setSignedIn(false);
|
|
123
|
-
window.location.href = ACCOUNT_LOGIN_HREF;
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
window.location.href = layout.header.signIn?.href ?? HEADER_SIGN_IN.href;
|
|
128
|
-
}, []);
|
|
129
|
-
|
|
130
|
-
return (
|
|
131
|
-
<weg-header
|
|
132
|
-
layout={layout}
|
|
133
|
-
signedIn={signedIn}
|
|
134
|
-
{...(userName ? { userName } : {})}
|
|
135
|
-
onWegAuthClick={onAuthClick}
|
|
136
|
-
/>
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
| Prop / event | Purpose |
|
|
142
|
-
| --- | --- |
|
|
143
|
-
| `signedIn={boolean}` | Switches to signed-in nav when `true` |
|
|
144
|
-
| `userName={string}` | First name beside profile icon on Manage Account |
|
|
145
|
-
| `onWegAuthClick` | Host handles routing / logout; call `event.preventDefault()` to override defaults |
|
|
146
|
-
|
|
147
|
-
**Ref fallback for the event** (if `onWegAuthClick` does not bind in your React version):
|
|
148
|
-
|
|
149
|
-
```tsx
|
|
150
|
-
import { useEffect, useRef } from 'react';
|
|
151
|
-
|
|
152
|
-
const ref = useRef<HTMLWegHeaderElement>(null);
|
|
153
|
-
|
|
154
|
-
useEffect(() => {
|
|
155
|
-
const el = ref.current;
|
|
156
|
-
if (!el) return;
|
|
157
|
-
|
|
158
|
-
const handler = (event: Event) => {
|
|
159
|
-
const e = event as CustomEvent<{ action: 'sign-in' | 'sign-out' }>;
|
|
160
|
-
e.preventDefault();
|
|
161
|
-
// ...
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
el.addEventListener('wegAuthClick', handler);
|
|
165
|
-
return () => el.removeEventListener('wegAuthClick', handler);
|
|
166
|
-
}, []);
|
|
167
|
-
|
|
168
|
-
return <weg-header ref={ref} layout={layout} signedIn={signedIn} userName="Alex" />;
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
## 4. Production: fetch layout from your API
|
|
172
|
-
|
|
173
|
-
```tsx
|
|
174
|
-
import { useEffect, useState } from 'react';
|
|
175
|
-
import 'weg-shared-layout/weg-header';
|
|
176
|
-
import 'weg-shared-layout/weg-footer';
|
|
177
|
-
import layoutFixture from 'weg-shared-layout/dummy-data.json';
|
|
178
|
-
|
|
179
|
-
type LayoutData = typeof layoutFixture;
|
|
180
|
-
|
|
181
|
-
const LAYOUT_URL = 'https://weg-payload-test.vercel.app/api/layout';
|
|
182
|
-
|
|
183
|
-
export function SiteLayout({ children }: { children: React.ReactNode }) {
|
|
184
|
-
const [layout, setLayout] = useState<LayoutData | null>(null);
|
|
185
|
-
|
|
186
|
-
useEffect(() => {
|
|
187
|
-
let cancelled = false;
|
|
188
|
-
fetch(LAYOUT_URL)
|
|
189
|
-
.then((res) => {
|
|
190
|
-
if (!res.ok) throw new Error(`layout fetch failed: ${res.status}`);
|
|
191
|
-
return res.json() as Promise<LayoutData>;
|
|
192
|
-
})
|
|
193
|
-
.then((data) => {
|
|
194
|
-
if (!cancelled) setLayout(data);
|
|
195
|
-
})
|
|
196
|
-
.catch(console.error);
|
|
197
|
-
return () => {
|
|
198
|
-
cancelled = true;
|
|
199
|
-
};
|
|
200
|
-
}, []);
|
|
201
|
-
|
|
202
|
-
if (!layout) return null;
|
|
203
|
-
|
|
204
|
-
return (
|
|
205
|
-
<>
|
|
206
|
-
<weg-header layout={layout} />
|
|
207
|
-
{children}
|
|
208
|
-
<weg-footer layout={layout} />
|
|
209
|
-
</>
|
|
210
|
-
);
|
|
211
|
-
}
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
Replace the URL with your CMS/API. Keep the object shape aligned with `dummy-data.json`.
|
|
215
|
-
|
|
216
|
-
## TypeScript: module augmentation
|
|
217
|
-
|
|
218
|
-
Add once (e.g. `src/types/weg-shared-layout-jsx.d.ts`):
|
|
219
|
-
|
|
220
|
-
```ts
|
|
221
|
-
import type { JSX as WegSharedLayoutJSX } from 'weg-shared-layout';
|
|
222
|
-
import type { DetailedHTMLProps, HTMLAttributes } from 'react';
|
|
223
|
-
|
|
224
|
-
declare module 'react' {
|
|
225
|
-
namespace JSX {
|
|
226
|
-
interface IntrinsicElements extends WegSharedLayoutJSX.IntrinsicElements {
|
|
227
|
-
'weg-header': WegSharedLayoutJSX.IntrinsicElements['weg-header'] &
|
|
228
|
-
DetailedHTMLProps<HTMLAttributes<HTMLWegHeaderElement>, HTMLWegHeaderElement>;
|
|
229
|
-
'weg-footer': WegSharedLayoutJSX.IntrinsicElements['weg-footer'] &
|
|
230
|
-
DetailedHTMLProps<HTMLAttributes<HTMLWegFooterElement>, HTMLWegFooterElement>;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
## `layout` prop vs attribute
|
|
237
|
-
|
|
238
|
-
| How data is set | Works with object? |
|
|
239
|
-
| --- | --- |
|
|
240
|
-
| **Property** `el.layout = obj` / React 19 `layout={obj}` | Yes |
|
|
241
|
-
| **Attribute** `layout="[object Object]"` / React 18 `layout={obj}` | No — components stay empty |
|
|
242
|
-
| **Attribute** `layout={JSON.stringify(obj)}` | Yes — component parses JSON |
|
|
243
|
-
|
|
244
|
-
## Troubleshooting
|
|
245
|
-
|
|
246
|
-
| Symptom | Likely cause | Fix |
|
|
247
|
-
| --- | --- | --- |
|
|
248
|
-
| Empty header/footer | Loader/tag import missing | `defineCustomElements()` + tag imports. |
|
|
249
|
-
| Empty despite correct data | React set `layout` as attribute | React 19+, or `JSON.stringify`, or ref assignment. |
|
|
250
|
-
| Logo missing on header | Old build without inlined logo | Upgrade package; logo is bundled in `logo-data.ts`. |
|
|
251
|
-
| Auth always Sign in | `signed-in` not set | Bind `signedIn={!!session}`. |
|
|
252
|
-
| Manage Account shows label not name | `user-name` not set | Pass `userName` when signed in. |
|
|
253
|
-
| `onWegAuthClick` not firing | React CE event binding | Use `addEventListener` on a ref. |
|
|
254
|
-
| TS: unknown element | No augmentation | Add `weg-shared-layout-jsx.d.ts`. |
|
|
255
|
-
|
|
256
|
-
## See also
|
|
257
|
-
|
|
258
|
-
- **[Next.js App Router](./nextjs.md)**
|
|
259
|
-
- **[Angular](./angular.md)**
|
|
260
|
-
- **[Plain HTML / vanilla JS](./vanilla.md)**
|
|
261
|
-
- **[Package readme](../readme.md)**
|
|
1
|
+
# React (SPA)
|
|
2
|
+
|
|
3
|
+
Guide for **client-rendered** React apps (Vite, Create React App, etc.). If you use **Next.js App Router**, see **[Next.js](./nextjs.md)** — SSR and Server Components require a different integration.
|
|
4
|
+
|
|
5
|
+
## Components
|
|
6
|
+
|
|
7
|
+
| Tag | Purpose |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| `<weg-header>` | Site header — bundled logo, CMS nav (signed out), built-in nav (signed in), Sign in / Manage Account / Sign out |
|
|
10
|
+
| `<weg-footer>` | Site footer — social links, columns, credits, copyright |
|
|
11
|
+
|
|
12
|
+
Both are **presentational** [Stencil](https://stenciljs.com/) Web Components. They **do not fetch data** — your app passes a **`layout`** payload (API, CMS, or [`dummy-data.json`](../src/assets/dummy-data.json)).
|
|
13
|
+
|
|
14
|
+
`<weg-header>` additionally accepts **`signed-in`**, **`user-name`**, and emits **`wegAuthClick`**.
|
|
15
|
+
|
|
16
|
+
## Requirements
|
|
17
|
+
|
|
18
|
+
| Requirement | Notes |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| **React 19+** | React 19 maps custom-element props to DOM **properties** when possible. Older React often sets props as string **attributes**, which breaks object payloads. |
|
|
21
|
+
| **Bundler** | Must resolve `node_modules` ESM/CJS from `weg-shared-layout`. |
|
|
22
|
+
| **TypeScript (optional)** | Module augmentation below; enable `resolveJsonModule` if you import `dummy-data.json`. |
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm i weg-shared-layout
|
|
28
|
+
# or
|
|
29
|
+
pnpm add weg-shared-layout
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## 1. Register custom elements (once)
|
|
33
|
+
|
|
34
|
+
Call `defineCustomElements()` **once** before the first render — typically in `main.tsx` / `index.tsx`:
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import { defineCustomElements } from 'weg-shared-layout/loader';
|
|
38
|
+
|
|
39
|
+
defineCustomElements();
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Side-effect import the tags your app uses:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import 'weg-shared-layout/weg-header';
|
|
46
|
+
import 'weg-shared-layout/weg-footer';
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## 2. Layout shell
|
|
50
|
+
|
|
51
|
+
### Recommended: pass `layout` as an object (React 19+)
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
import 'weg-shared-layout/weg-header';
|
|
55
|
+
import 'weg-shared-layout/weg-footer';
|
|
56
|
+
import layout from 'weg-shared-layout/dummy-data.json';
|
|
57
|
+
|
|
58
|
+
export function SiteLayout({ children }: { children: React.ReactNode }) {
|
|
59
|
+
return (
|
|
60
|
+
<>
|
|
61
|
+
<weg-header layout={layout} />
|
|
62
|
+
{children}
|
|
63
|
+
<weg-footer layout={layout} />
|
|
64
|
+
</>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Fallback: `JSON.stringify` (React 18 or empty components)
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
<weg-header layout={JSON.stringify(layout)} />
|
|
73
|
+
<weg-footer layout={JSON.stringify(layout)} />
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## 3. Header auth
|
|
77
|
+
|
|
78
|
+
Define auth URLs once in your app:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
// auth.ts
|
|
82
|
+
export const HEADER_SIGN_IN = {
|
|
83
|
+
label: 'Sign in',
|
|
84
|
+
href: 'https://account.warwickemploymentgroup.com/account/login',
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const ACCOUNT_LOGIN_HREF = HEADER_SIGN_IN.href;
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Signed out
|
|
91
|
+
|
|
92
|
+
Pass CMS/API layout with `dropdowns`, `links`, and `signIn`. Use [`dummy-data.json`](../src/assets/dummy-data.json) for the full shape. Keep auth URLs in a local file:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
// auth.ts (host app)
|
|
96
|
+
export const HEADER_SIGN_IN = {
|
|
97
|
+
label: 'Sign in',
|
|
98
|
+
href: 'https://account.warwickemploymentgroup.com/account/login',
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export const ACCOUNT_LOGIN_HREF = HEADER_SIGN_IN.href;
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Signed in
|
|
105
|
+
|
|
106
|
+
Set **`signed-in`** and optionally **`user-name`**. The component **ignores CMS nav** and shows built-in links: Find a job, Dashboard, Manage Account, Sign out.
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
import { useCallback, useState } from 'react';
|
|
110
|
+
import { ACCOUNT_LOGIN_HREF, HEADER_SIGN_IN } from './auth';
|
|
111
|
+
import 'weg-shared-layout/weg-header';
|
|
112
|
+
import layout from 'weg-shared-layout/dummy-data.json';
|
|
113
|
+
|
|
114
|
+
export function SiteHeader() {
|
|
115
|
+
const [signedIn, setSignedIn] = useState(false);
|
|
116
|
+
const userName = signedIn ? 'Alex' : undefined;
|
|
117
|
+
|
|
118
|
+
const onAuthClick = useCallback((event: CustomEvent<{ action: 'sign-in' | 'sign-out' }>) => {
|
|
119
|
+
event.preventDefault();
|
|
120
|
+
|
|
121
|
+
if (event.detail.action === 'sign-out') {
|
|
122
|
+
setSignedIn(false);
|
|
123
|
+
window.location.href = ACCOUNT_LOGIN_HREF;
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
window.location.href = layout.header.signIn?.href ?? HEADER_SIGN_IN.href;
|
|
128
|
+
}, []);
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<weg-header
|
|
132
|
+
layout={layout}
|
|
133
|
+
signedIn={signedIn}
|
|
134
|
+
{...(userName ? { userName } : {})}
|
|
135
|
+
onWegAuthClick={onAuthClick}
|
|
136
|
+
/>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
| Prop / event | Purpose |
|
|
142
|
+
| --- | --- |
|
|
143
|
+
| `signedIn={boolean}` | Switches to signed-in nav when `true` |
|
|
144
|
+
| `userName={string}` | First name beside profile icon on Manage Account |
|
|
145
|
+
| `onWegAuthClick` | Host handles routing / logout; call `event.preventDefault()` to override defaults |
|
|
146
|
+
|
|
147
|
+
**Ref fallback for the event** (if `onWegAuthClick` does not bind in your React version):
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
import { useEffect, useRef } from 'react';
|
|
151
|
+
|
|
152
|
+
const ref = useRef<HTMLWegHeaderElement>(null);
|
|
153
|
+
|
|
154
|
+
useEffect(() => {
|
|
155
|
+
const el = ref.current;
|
|
156
|
+
if (!el) return;
|
|
157
|
+
|
|
158
|
+
const handler = (event: Event) => {
|
|
159
|
+
const e = event as CustomEvent<{ action: 'sign-in' | 'sign-out' }>;
|
|
160
|
+
e.preventDefault();
|
|
161
|
+
// ...
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
el.addEventListener('wegAuthClick', handler);
|
|
165
|
+
return () => el.removeEventListener('wegAuthClick', handler);
|
|
166
|
+
}, []);
|
|
167
|
+
|
|
168
|
+
return <weg-header ref={ref} layout={layout} signedIn={signedIn} userName="Alex" />;
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## 4. Production: fetch layout from your API
|
|
172
|
+
|
|
173
|
+
```tsx
|
|
174
|
+
import { useEffect, useState } from 'react';
|
|
175
|
+
import 'weg-shared-layout/weg-header';
|
|
176
|
+
import 'weg-shared-layout/weg-footer';
|
|
177
|
+
import layoutFixture from 'weg-shared-layout/dummy-data.json';
|
|
178
|
+
|
|
179
|
+
type LayoutData = typeof layoutFixture;
|
|
180
|
+
|
|
181
|
+
const LAYOUT_URL = 'https://weg-payload-test.vercel.app/api/layout';
|
|
182
|
+
|
|
183
|
+
export function SiteLayout({ children }: { children: React.ReactNode }) {
|
|
184
|
+
const [layout, setLayout] = useState<LayoutData | null>(null);
|
|
185
|
+
|
|
186
|
+
useEffect(() => {
|
|
187
|
+
let cancelled = false;
|
|
188
|
+
fetch(LAYOUT_URL)
|
|
189
|
+
.then((res) => {
|
|
190
|
+
if (!res.ok) throw new Error(`layout fetch failed: ${res.status}`);
|
|
191
|
+
return res.json() as Promise<LayoutData>;
|
|
192
|
+
})
|
|
193
|
+
.then((data) => {
|
|
194
|
+
if (!cancelled) setLayout(data);
|
|
195
|
+
})
|
|
196
|
+
.catch(console.error);
|
|
197
|
+
return () => {
|
|
198
|
+
cancelled = true;
|
|
199
|
+
};
|
|
200
|
+
}, []);
|
|
201
|
+
|
|
202
|
+
if (!layout) return null;
|
|
203
|
+
|
|
204
|
+
return (
|
|
205
|
+
<>
|
|
206
|
+
<weg-header layout={layout} />
|
|
207
|
+
{children}
|
|
208
|
+
<weg-footer layout={layout} />
|
|
209
|
+
</>
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Replace the URL with your CMS/API. Keep the object shape aligned with `dummy-data.json`.
|
|
215
|
+
|
|
216
|
+
## TypeScript: module augmentation
|
|
217
|
+
|
|
218
|
+
Add once (e.g. `src/types/weg-shared-layout-jsx.d.ts`):
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
import type { JSX as WegSharedLayoutJSX } from 'weg-shared-layout';
|
|
222
|
+
import type { DetailedHTMLProps, HTMLAttributes } from 'react';
|
|
223
|
+
|
|
224
|
+
declare module 'react' {
|
|
225
|
+
namespace JSX {
|
|
226
|
+
interface IntrinsicElements extends WegSharedLayoutJSX.IntrinsicElements {
|
|
227
|
+
'weg-header': WegSharedLayoutJSX.IntrinsicElements['weg-header'] &
|
|
228
|
+
DetailedHTMLProps<HTMLAttributes<HTMLWegHeaderElement>, HTMLWegHeaderElement>;
|
|
229
|
+
'weg-footer': WegSharedLayoutJSX.IntrinsicElements['weg-footer'] &
|
|
230
|
+
DetailedHTMLProps<HTMLAttributes<HTMLWegFooterElement>, HTMLWegFooterElement>;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## `layout` prop vs attribute
|
|
237
|
+
|
|
238
|
+
| How data is set | Works with object? |
|
|
239
|
+
| --- | --- |
|
|
240
|
+
| **Property** `el.layout = obj` / React 19 `layout={obj}` | Yes |
|
|
241
|
+
| **Attribute** `layout="[object Object]"` / React 18 `layout={obj}` | No — components stay empty |
|
|
242
|
+
| **Attribute** `layout={JSON.stringify(obj)}` | Yes — component parses JSON |
|
|
243
|
+
|
|
244
|
+
## Troubleshooting
|
|
245
|
+
|
|
246
|
+
| Symptom | Likely cause | Fix |
|
|
247
|
+
| --- | --- | --- |
|
|
248
|
+
| Empty header/footer | Loader/tag import missing | `defineCustomElements()` + tag imports. |
|
|
249
|
+
| Empty despite correct data | React set `layout` as attribute | React 19+, or `JSON.stringify`, or ref assignment. |
|
|
250
|
+
| Logo missing on header | Old build without inlined logo | Upgrade package; logo is bundled in `logo-data.ts`. |
|
|
251
|
+
| Auth always Sign in | `signed-in` not set | Bind `signedIn={!!session}`. |
|
|
252
|
+
| Manage Account shows label not name | `user-name` not set | Pass `userName` when signed in. |
|
|
253
|
+
| `onWegAuthClick` not firing | React CE event binding | Use `addEventListener` on a ref. |
|
|
254
|
+
| TS: unknown element | No augmentation | Add `weg-shared-layout-jsx.d.ts`. |
|
|
255
|
+
|
|
256
|
+
## See also
|
|
257
|
+
|
|
258
|
+
- **[Next.js App Router](./nextjs.md)**
|
|
259
|
+
- **[Angular](./angular.md)**
|
|
260
|
+
- **[Plain HTML / vanilla JS](./vanilla.md)**
|
|
261
|
+
- **[Package readme](../readme.md)**
|