@eox/pages-theme-eox 0.11.5 → 1.1.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/.gitlab-ci.yml +30 -0
- package/.release-it.json +15 -0
- package/CHANGELOG.md +19 -0
- package/README.md +274 -0
- package/cypress/support/commands.js +25 -0
- package/cypress/support/component-index.html +11 -0
- package/cypress/support/component.js +22 -0
- package/cypress/support/mocks/features.data.js +9 -0
- package/cypress/support/mocks/helpers.js +63 -0
- package/cypress/support/mocks/vitepress.js +83 -0
- package/cypress.config.js +46 -0
- package/package.json +12 -2
- package/src/components/CTASection.cy.js +72 -0
- package/src/components/CookieBanner.cy.js +49 -0
- package/src/components/CookieBanner.vue +19 -6
- package/src/components/CookieSettings.cy.js +73 -0
- package/src/components/CookieSettings.vue +35 -27
- package/src/components/DataTable.cy.js +58 -0
- package/src/components/DataTable.vue +5 -1
- package/src/components/FeatureCard.cy.js +83 -0
- package/src/components/FeatureCard.vue +11 -5
- package/src/components/FeatureSection.cy.js +63 -0
- package/src/components/FeatureSection.vue +8 -4
- package/src/components/FeaturesGallery.cy.js +45 -0
- package/src/components/FeaturesGallery.vue +6 -3
- package/src/components/Footer.cy.js +77 -0
- package/src/components/Footer.vue +35 -14
- package/src/components/HeroSection.cy.js +66 -0
- package/src/components/LogoSection.cy.js +53 -0
- package/src/components/MobileNavDropdown.cy.js +67 -0
- package/src/components/MobileNavDropdown.vue +48 -0
- package/src/components/NavBar.cy.js +143 -0
- package/src/components/NavBar.vue +144 -8
- package/src/components/NavDropdown.cy.js +71 -0
- package/src/components/NavDropdown.vue +62 -0
- package/src/components/NewsBanner.cy.js +23 -0
- package/src/components/NewsBanner.vue +0 -1
- package/src/components/NotFound.cy.js +16 -0
- package/src/components/NotFound.vue +8 -2
- package/src/components/PricingTable.cy.js +91 -0
- package/src/components/PricingTable.vue +24 -7
- package/src/components/Tutorial.cy.js +85 -0
- package/src/components/Tutorial.vue +162 -0
- package/src/helpers.js +78 -0
- package/src/index.js +3 -0
- package/src/style.css +121 -4
- package/src/vitepressConfig.mjs +141 -95
package/.gitlab-ci.yml
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
image: cypress/base:20.14.0
|
|
2
|
+
|
|
3
|
+
stages:
|
|
4
|
+
- test
|
|
5
|
+
|
|
6
|
+
cache:
|
|
7
|
+
key:
|
|
8
|
+
files:
|
|
9
|
+
- package-lock.json
|
|
10
|
+
paths:
|
|
11
|
+
- .npm/
|
|
12
|
+
|
|
13
|
+
variables:
|
|
14
|
+
npm_config_cache: "$CI_PROJECT_DIR/.npm"
|
|
15
|
+
|
|
16
|
+
format:
|
|
17
|
+
stage: test
|
|
18
|
+
script:
|
|
19
|
+
- npm ci
|
|
20
|
+
- npm run format:check
|
|
21
|
+
rules:
|
|
22
|
+
- if: $CI_PIPELINE_SOURCE == 'merge_request_event' && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == 'main'
|
|
23
|
+
|
|
24
|
+
test:
|
|
25
|
+
stage: test
|
|
26
|
+
script:
|
|
27
|
+
- npm ci
|
|
28
|
+
- npm run test:component
|
|
29
|
+
rules:
|
|
30
|
+
- if: $CI_PIPELINE_SOURCE == 'merge_request_event' && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == 'main'
|
package/.release-it.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"git": {
|
|
3
|
+
"commitMessage": "chore: release v${version}",
|
|
4
|
+
"tagName": "v${version}"
|
|
5
|
+
},
|
|
6
|
+
"npm": {
|
|
7
|
+
"publish": false
|
|
8
|
+
},
|
|
9
|
+
"plugins": {
|
|
10
|
+
"@release-it/conventional-changelog": {
|
|
11
|
+
"preset": "angular",
|
|
12
|
+
"infile": "CHANGELOG.md"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
# [1.1.0](https://gitlab.eox.at/eox/hub/eoxhub-portal/compare/v1.0.0...v1.1.0) (2026-02-16)
|
|
4
|
+
|
|
5
|
+
### Bug Fixes
|
|
6
|
+
|
|
7
|
+
- **style:** css adjustments ([f2dc9a1](https://gitlab.eox.at/eox/hub/eoxhub-portal/commit/f2dc9a1db294b018e49c07acdb0f030bd98e03bf))
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
- i18n support ([27af579](https://gitlab.eox.at/eox/hub/eoxhub-portal/commit/27af579611046e81baac7f49e4dd05db8e1cac12))
|
|
12
|
+
- introduce tutorial component ([158c7fd](https://gitlab.eox.at/eox/hub/eoxhub-portal/commit/158c7fdeb137f7b5598e0aa9819cf4793a5c8d81))
|
|
13
|
+
|
|
14
|
+
# [1.0.0](https://gitlab.eox.at/eox/hub/eoxhub-portal/compare/v0.11.5...v1.0.0) (2026-01-20)
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
- add support for TOC, socialLinks, sidebar and outline ([c512361](https://gitlab.eox.at/eox/hub/eoxhub-portal/commit/c51236122a65faf2edb6e8a575d5ef53d86c25af))
|
|
19
|
+
- support for (nested) dropdown nav entries ([71b531e](https://gitlab.eox.at/eox/hub/eoxhub-portal/commit/71b531e1193b18e1fa33397916d0b5a0d35df128))
|
package/README.md
CHANGED
|
@@ -16,3 +16,277 @@ export default {
|
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
See also the [Vitepress Docs](https://vitepress.dev/guide/custom-theme#consuming-a-custom-theme).
|
|
19
|
+
|
|
20
|
+
## Configuration
|
|
21
|
+
|
|
22
|
+
This theme supports standard [VitePress Default Theme Configuration](https://vitepress.dev/reference/default-theme-config) with some specific adaptations.
|
|
23
|
+
|
|
24
|
+
### Navigation Bar
|
|
25
|
+
|
|
26
|
+
The Navigation Bar follows the [Default Theme Nav](https://vitepress.dev/reference/default-theme-nav) structure with the following differences:
|
|
27
|
+
|
|
28
|
+
| Feature | Supported | Notes |
|
|
29
|
+
| :---------------- | :-------- | :------------------------------------------------------------------------------------------ |
|
|
30
|
+
| Links | ✅ | Standard `{ text, link }` structure. |
|
|
31
|
+
| Dropdowns | ✅ | Standard `{ text, items: [] }` structure, supports nesting. |
|
|
32
|
+
| Active Match | ✅ | `activeMatch` regex strings are supported. |
|
|
33
|
+
| Target/Rel | ✅ | `target` and `rel` attributes are supported. |
|
|
34
|
+
| Buttons | ✨ | **Custom**: Add `action: 'primary'` (or 'secondary') to a nav item to style it as a button. |
|
|
35
|
+
| Logo | ⚠️ | Must be an object with light/dark paths: `{ light: '...', dark: '...' }`. |
|
|
36
|
+
| Social Links | ✅ | Valid `socialLinks` will be displayed in the navbar. |
|
|
37
|
+
| Custom Components | ❌ | Embedding custom components directly in the nav array is not supported. |
|
|
38
|
+
|
|
39
|
+
#### Example Configuration
|
|
40
|
+
|
|
41
|
+
```javascript
|
|
42
|
+
// .vitepress/config.js
|
|
43
|
+
export default {
|
|
44
|
+
themeConfig: {
|
|
45
|
+
logo: {
|
|
46
|
+
light: "/logo-light.svg",
|
|
47
|
+
dark: "/logo-dark.svg",
|
|
48
|
+
},
|
|
49
|
+
nav: [
|
|
50
|
+
{ text: "Home", link: "/" },
|
|
51
|
+
{
|
|
52
|
+
text: "Products",
|
|
53
|
+
items: [
|
|
54
|
+
{ text: "Product A", link: "/a" },
|
|
55
|
+
{ text: "Product B", link: "/b" },
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
// Custom generic button style
|
|
59
|
+
{ text: "Contact Us", link: "/contact", action: "primary" },
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### General Configuration
|
|
66
|
+
|
|
67
|
+
The following standard VitePress configurations are handled as follows:
|
|
68
|
+
|
|
69
|
+
- **Site Title**: Explicitly disabled (`siteTitle: false`). The theme relies on the logo for branding.
|
|
70
|
+
- **Logo**: Used for branding. Must be configured as an object `{ light: string, dark: string }`.
|
|
71
|
+
- **Nav**: Fully supported (see above).
|
|
72
|
+
- **Footer**: Supported. The theme uses `theme.footer.copyright` for the copyright text.
|
|
73
|
+
- **Dark Mode**: The theme enforces a specific appearance. `appearance: false` is set in the base config.
|
|
74
|
+
|
|
75
|
+
### Internationalization (i18n)
|
|
76
|
+
|
|
77
|
+
This theme leverages VitePress's native [i18n capabilities](https://vitepress.dev/guide/i18n) and extends them with a simplified configuration via `brandConfig`.
|
|
78
|
+
|
|
79
|
+
#### Multi-language Routing
|
|
80
|
+
|
|
81
|
+
You can define multiple locales in your `brandConfig`. The theme will automatically generate the corresponding VitePress routes and display a language switcher in the navigation bar.
|
|
82
|
+
|
|
83
|
+
```javascript
|
|
84
|
+
// brandConfig
|
|
85
|
+
{
|
|
86
|
+
i18n: {
|
|
87
|
+
locale: {
|
|
88
|
+
en: {
|
|
89
|
+
"Read more about": "Read more about",
|
|
90
|
+
"Contact us": "Contact us"
|
|
91
|
+
},
|
|
92
|
+
de: {
|
|
93
|
+
"Read more about": "Mehr lesen über",
|
|
94
|
+
"Contact us": "Kontaktieren Sie uns"
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
currentLocale: "en" // This language will be at the root URL (/)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
#### Translation Pattern
|
|
103
|
+
|
|
104
|
+
The theme uses a "string-as-key" pattern. Most hardcoded UI elements (buttons, footer labels, etc.) use their English text as a lookup key. If a translation is provided in the `brandConfig.i18n.locale` object, it will be used; otherwise, it falls back to the original English text.
|
|
105
|
+
|
|
106
|
+
Nested keys are also supported via dot-notation if needed (e.g., `legal.privacy`).
|
|
107
|
+
|
|
108
|
+
#### Language Switcher
|
|
109
|
+
|
|
110
|
+
- **Desktop**: Appears as a translate icon in the top navigation bar.
|
|
111
|
+
- **Mobile**: Appears as a dedicated section within the mobile menu.
|
|
112
|
+
- The current language is automatically detected based on the URL path (e.g., `/de/` for German).
|
|
113
|
+
|
|
114
|
+
**Note on Documentation Features**:
|
|
115
|
+
This theme is primarily designed for landing pages and product showcases.
|
|
116
|
+
|
|
117
|
+
- **Social Links** and **TOC** are supported as described in the VitePress documentation.
|
|
118
|
+
- **Sidebar** and **Outline** are automatically enabled for the `doc` layout, whereas the `page` layout doesn't have them.
|
|
119
|
+
- Other features like **Edit Link**, **Last Updated**, and **Algolia Search** are **not** currently integrated into the custom `Layout.vue` and may not function as expected if configured.
|
|
120
|
+
|
|
121
|
+
### Cookie Consent
|
|
122
|
+
|
|
123
|
+
This theme includes built-in cookie consent management integrated with Matomo (Piwik) analytics.
|
|
124
|
+
|
|
125
|
+
- **Cookie Banner**: Automatically displayed if `brandConfig.analytics` is present.
|
|
126
|
+
- **Cookie Settings**: a `/cookie-settings` page is automatically handled to allow users to review and toggle optional cookies.
|
|
127
|
+
|
|
128
|
+
#### Analytics Configuration
|
|
129
|
+
|
|
130
|
+
To enable the cookie banner and analytics tracking, ensure your brand config includes:
|
|
131
|
+
|
|
132
|
+
```javascript
|
|
133
|
+
// brandConfig
|
|
134
|
+
{
|
|
135
|
+
analytics: {
|
|
136
|
+
siteId: "YOUR_MATOMO_SITE_ID";
|
|
137
|
+
// The tracker URL is hardcoded to "https://nix.eox.at/piwik/" in vitepressConfig.mjs
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Layout & Components
|
|
143
|
+
|
|
144
|
+
The theme provides several built-in components and layout features. Some are driven by frontmatter configuration, while others can be used directly in your Markdown files.
|
|
145
|
+
|
|
146
|
+
### Hero Section
|
|
147
|
+
|
|
148
|
+
To add a Hero section to the top of a page, use the `hero` frontmatter object.
|
|
149
|
+
|
|
150
|
+
```yaml
|
|
151
|
+
---
|
|
152
|
+
hero:
|
|
153
|
+
title: "Welcome to My Site"
|
|
154
|
+
tagline: "Building the future of things"
|
|
155
|
+
image:
|
|
156
|
+
src: "/hero-image.png"
|
|
157
|
+
alt: "Hero Image"
|
|
158
|
+
actions:
|
|
159
|
+
- text: "Get Started"
|
|
160
|
+
link: "/start"
|
|
161
|
+
type: "primary"
|
|
162
|
+
- text: "Learn More"
|
|
163
|
+
link: "/about"
|
|
164
|
+
type: "secondary"
|
|
165
|
+
---
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Feature Section
|
|
169
|
+
|
|
170
|
+
Use the `<FeatureSection>` component to highlight specific features.
|
|
171
|
+
|
|
172
|
+
```html
|
|
173
|
+
<FeatureSection
|
|
174
|
+
title="My Feature"
|
|
175
|
+
tagline="This is an amazing feature"
|
|
176
|
+
icon="mdi-star"
|
|
177
|
+
flipped
|
|
178
|
+
>
|
|
179
|
+
<template #default>
|
|
180
|
+
<p>Detailed description of the feature.</p>
|
|
181
|
+
</template>
|
|
182
|
+
<template #actions>
|
|
183
|
+
<a href="/docs" class="btn">Documentation</a>
|
|
184
|
+
</template>
|
|
185
|
+
</FeatureSection>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Pricing Table
|
|
189
|
+
|
|
190
|
+
A flexible pricing table component.
|
|
191
|
+
|
|
192
|
+
```html
|
|
193
|
+
<PricingTable
|
|
194
|
+
:config="{
|
|
195
|
+
plans: [
|
|
196
|
+
{
|
|
197
|
+
title: 'Basic',
|
|
198
|
+
price: '€0',
|
|
199
|
+
features: ['Feature 1', 'Feature 2'],
|
|
200
|
+
link: { text: 'Sign Up', href: '/signup' }
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
title: 'Pro',
|
|
204
|
+
price: '€99',
|
|
205
|
+
features: ['Everything in Basic', 'Feature 3'],
|
|
206
|
+
link: { text: 'Go Pro', href: '/pro' },
|
|
207
|
+
active: true
|
|
208
|
+
}
|
|
209
|
+
]
|
|
210
|
+
}"
|
|
211
|
+
/>
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Other Components
|
|
215
|
+
|
|
216
|
+
#### CTA Section
|
|
217
|
+
|
|
218
|
+
A Call-to-Action section typically placed at the bottom of a page to encourage user engagement.
|
|
219
|
+
|
|
220
|
+
```html
|
|
221
|
+
<CTASection
|
|
222
|
+
title="Ready to get started?"
|
|
223
|
+
tagline="Join thousands of others today."
|
|
224
|
+
primary-button="Sign Up Now"
|
|
225
|
+
primary-link="/register"
|
|
226
|
+
secondary-button="Contact Sales"
|
|
227
|
+
secondary-link="/sales"
|
|
228
|
+
alt-button="Read Documentation"
|
|
229
|
+
alt-link="/docs"
|
|
230
|
+
dark
|
|
231
|
+
/>
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
#### Logo Section
|
|
235
|
+
|
|
236
|
+
Displays a grid of client or partner logos. It automatically handles logo sizing.
|
|
237
|
+
|
|
238
|
+
```html
|
|
239
|
+
<LogoSection
|
|
240
|
+
:logos="[
|
|
241
|
+
{ image: '/logos/client1.png', alt: 'Client 1', link: 'https://client1.com' },
|
|
242
|
+
{ image: '/logos/client2.png', alt: 'Client 2' }
|
|
243
|
+
]"
|
|
244
|
+
base-height="4"
|
|
245
|
+
strength="1"
|
|
246
|
+
/>
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
#### Data Table
|
|
250
|
+
|
|
251
|
+
An interactive table with expandable rows. The `summary` object keys must match the `headers` provided.
|
|
252
|
+
|
|
253
|
+
```html
|
|
254
|
+
<DataTable
|
|
255
|
+
:headers="['Name', 'Type', 'Status']"
|
|
256
|
+
:data="[
|
|
257
|
+
{
|
|
258
|
+
summary: { Name: 'Task A', Type: 'Cron', Status: 'Active' },
|
|
259
|
+
content: 'Detailed description of Task A...'
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
summary: { Name: 'Task B', Type: 'Trigger', Status: 'Paused' },
|
|
263
|
+
content: 'Detailed description of Task B...'
|
|
264
|
+
}
|
|
265
|
+
]"
|
|
266
|
+
/>
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
#### Features Gallery
|
|
270
|
+
|
|
271
|
+
A grid layout for feature cards. Automatically used on pages under `features/`, but can be used manually with custom cards.
|
|
272
|
+
|
|
273
|
+
```html
|
|
274
|
+
<FeaturesGallery
|
|
275
|
+
section-title="All Features"
|
|
276
|
+
background="surface-container-low"
|
|
277
|
+
:cards="[
|
|
278
|
+
{
|
|
279
|
+
id: 1,
|
|
280
|
+
title: 'Global Coverage',
|
|
281
|
+
content: 'Access data from anywhere.',
|
|
282
|
+
icon: 'mdi-earth'
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
id: 2,
|
|
286
|
+
title: 'Real-time',
|
|
287
|
+
content: 'Updates as they happen.',
|
|
288
|
+
link: { text: 'Learn more', href: '/real-time' }
|
|
289
|
+
}
|
|
290
|
+
]"
|
|
291
|
+
/>
|
|
292
|
+
```
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// ***********************************************
|
|
2
|
+
// This example commands.js shows you how to
|
|
3
|
+
// create various custom commands and overwrite
|
|
4
|
+
// existing commands.
|
|
5
|
+
//
|
|
6
|
+
// For more comprehensive examples of custom
|
|
7
|
+
// commands please read more here:
|
|
8
|
+
// https://on.cypress.io/custom-commands
|
|
9
|
+
// ***********************************************
|
|
10
|
+
//
|
|
11
|
+
//
|
|
12
|
+
// -- This is a parent command --
|
|
13
|
+
// Cypress.Commands.add('login', (email, password) => { ... })
|
|
14
|
+
//
|
|
15
|
+
//
|
|
16
|
+
// -- This is a child command --
|
|
17
|
+
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
|
|
18
|
+
//
|
|
19
|
+
//
|
|
20
|
+
// -- This is a dual command --
|
|
21
|
+
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
|
|
22
|
+
//
|
|
23
|
+
//
|
|
24
|
+
// -- This will overwrite an existing command --
|
|
25
|
+
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Import commands.js using ES2015 syntax:
|
|
2
|
+
import "./commands";
|
|
3
|
+
|
|
4
|
+
// Alternatively you can use CommonJS syntax:
|
|
5
|
+
// require('./commands')
|
|
6
|
+
|
|
7
|
+
import { mount } from "cypress/vue";
|
|
8
|
+
|
|
9
|
+
// Import global styles
|
|
10
|
+
import "../../src/style.css";
|
|
11
|
+
|
|
12
|
+
// Import EOX UI web components
|
|
13
|
+
import "@eox/ui";
|
|
14
|
+
|
|
15
|
+
// Ensure global styles are applied
|
|
16
|
+
// This might be needed if vite config doesn't pick up the CSS the same way
|
|
17
|
+
// But importing it here usually works for component testing
|
|
18
|
+
|
|
19
|
+
Cypress.Commands.add("mount", mount);
|
|
20
|
+
|
|
21
|
+
// Example use:
|
|
22
|
+
// cy.mount(MyComponent)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Mock generic helpers
|
|
2
|
+
// We delegate to a global object to allow spying in tests
|
|
3
|
+
window.__helpersMock = {
|
|
4
|
+
trackEvent: (details) => {},
|
|
5
|
+
acceptCookies: (router) => {},
|
|
6
|
+
declineCookies: (router) => {},
|
|
7
|
+
enableTracking: (enable, router) => {},
|
|
8
|
+
showBanner: (show) => {
|
|
9
|
+
// Basic DOM simulation for banner visibility
|
|
10
|
+
if (typeof document !== "undefined") {
|
|
11
|
+
const banner = document.querySelector(".cookie-banner");
|
|
12
|
+
if (banner) {
|
|
13
|
+
banner.style.display = show ? "block" : "none";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
isActive: (item, path) => {
|
|
18
|
+
if (item.link === path) return true;
|
|
19
|
+
if (item.items) {
|
|
20
|
+
return item.items.some((sub) => window.__helpersMock.isActive(sub, path));
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
},
|
|
24
|
+
getFlatList: (items) => {
|
|
25
|
+
let list = [];
|
|
26
|
+
items?.forEach((item) => {
|
|
27
|
+
if (item.link) {
|
|
28
|
+
list.push(item);
|
|
29
|
+
}
|
|
30
|
+
if (item.items) {
|
|
31
|
+
list = [...list, ...window.__helpersMock.getFlatList(item.items)];
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
return list;
|
|
35
|
+
},
|
|
36
|
+
t: (key, i18n) => {
|
|
37
|
+
if (!i18n) return key;
|
|
38
|
+
const keys = key.split(".");
|
|
39
|
+
let result = i18n;
|
|
40
|
+
for (const k of keys) {
|
|
41
|
+
if (result && Object.prototype.hasOwnProperty.call(result, k)) {
|
|
42
|
+
result = result[k];
|
|
43
|
+
} else {
|
|
44
|
+
result = null;
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return result ? result : key;
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const trackEvent = (...args) => window.__helpersMock.trackEvent(...args);
|
|
53
|
+
export const acceptCookies = (...args) =>
|
|
54
|
+
window.__helpersMock.acceptCookies(...args);
|
|
55
|
+
export const declineCookies = (...args) =>
|
|
56
|
+
window.__helpersMock.declineCookies(...args);
|
|
57
|
+
export const enableTracking = (...args) =>
|
|
58
|
+
window.__helpersMock.enableTracking(...args);
|
|
59
|
+
export const showBanner = (...args) => window.__helpersMock.showBanner(...args);
|
|
60
|
+
export const isActive = (...args) => window.__helpersMock.isActive(...args);
|
|
61
|
+
export const getFlatList = (...args) =>
|
|
62
|
+
window.__helpersMock.getFlatList(...args);
|
|
63
|
+
export const t = (...args) => window.__helpersMock.t(...args);
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { reactive, ref } from "vue";
|
|
2
|
+
|
|
3
|
+
const site = ref({
|
|
4
|
+
title: "Test Site",
|
|
5
|
+
description: "Test Description",
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
const page = ref({
|
|
9
|
+
relativePath: "index.md",
|
|
10
|
+
title: "Test Page",
|
|
11
|
+
frontmatter: {},
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const theme = ref({
|
|
15
|
+
logo: { light: "/logo.png", dark: "/logo-dark.png" },
|
|
16
|
+
nav: [],
|
|
17
|
+
footer: { copyright: "Test Copyright" },
|
|
18
|
+
theme: {
|
|
19
|
+
brandConfig: {},
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const frontmatter = ref({});
|
|
24
|
+
|
|
25
|
+
export function useData() {
|
|
26
|
+
return {
|
|
27
|
+
site,
|
|
28
|
+
page,
|
|
29
|
+
theme,
|
|
30
|
+
frontmatter,
|
|
31
|
+
lang: ref("en-US"),
|
|
32
|
+
localeIndex: ref("root"),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function withBase(path) {
|
|
37
|
+
return path;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Helper to update mock data from tests
|
|
41
|
+
export function __setMockData(newData) {
|
|
42
|
+
if (newData.site) site.value = { ...site.value, ...newData.site };
|
|
43
|
+
if (newData.page) page.value = { ...page.value, ...newData.page };
|
|
44
|
+
if (newData.theme) theme.value = { ...theme.value, ...newData.theme };
|
|
45
|
+
if (newData.frontmatter)
|
|
46
|
+
frontmatter.value = { ...frontmatter.value, ...newData.frontmatter };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Mock route
|
|
50
|
+
const route = reactive({
|
|
51
|
+
path: "/",
|
|
52
|
+
data: {
|
|
53
|
+
title: "Test Page",
|
|
54
|
+
relativePath: "index.md",
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
export function useRoute() {
|
|
59
|
+
return route;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Mock useRouter
|
|
63
|
+
export function useRouter() {
|
|
64
|
+
return {
|
|
65
|
+
route,
|
|
66
|
+
go: () => {},
|
|
67
|
+
// Mock onBeforeRouteChange hook registry
|
|
68
|
+
onBeforeRouteChange: (fn) => {
|
|
69
|
+
// We can expose this to tests if needed to trigger it manually
|
|
70
|
+
window.__mockOnBeforeRouteChange = fn;
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function createContentLoader() {
|
|
76
|
+
return {
|
|
77
|
+
load: () => Promise.resolve([]),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function __setRouteMock(newRoute) {
|
|
82
|
+
Object.assign(route, newRoute);
|
|
83
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { defineConfig } from "cypress";
|
|
2
|
+
import vue from "@vitejs/plugin-vue";
|
|
3
|
+
import path from "path";
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
component: {
|
|
7
|
+
devServer: {
|
|
8
|
+
framework: "vue",
|
|
9
|
+
bundler: "vite",
|
|
10
|
+
viteConfig: {
|
|
11
|
+
resolve: {
|
|
12
|
+
alias: {
|
|
13
|
+
// Alias vitepress to our mock
|
|
14
|
+
vitepress: path.resolve(
|
|
15
|
+
__dirname,
|
|
16
|
+
"cypress/support/mocks/vitepress.js",
|
|
17
|
+
),
|
|
18
|
+
// Alias helpers to our mock
|
|
19
|
+
"../helpers": path.resolve(
|
|
20
|
+
__dirname,
|
|
21
|
+
"cypress/support/mocks/helpers.js",
|
|
22
|
+
),
|
|
23
|
+
"../helpers.js": path.resolve(
|
|
24
|
+
__dirname,
|
|
25
|
+
"cypress/support/mocks/helpers.js",
|
|
26
|
+
),
|
|
27
|
+
// Alias features.data.js to our mock
|
|
28
|
+
"../features.data.js": path.resolve(
|
|
29
|
+
__dirname,
|
|
30
|
+
"cypress/support/mocks/features.data.js",
|
|
31
|
+
),
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
plugins: [
|
|
35
|
+
vue({
|
|
36
|
+
template: {
|
|
37
|
+
compilerOptions: {
|
|
38
|
+
isCustomElement: (tag) => tag.includes("-"),
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
}),
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
});
|
package/package.json
CHANGED
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eox/pages-theme-eox",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Vitepress Theme with EOX branding",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"author": "EOX",
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"scripts": {
|
|
10
|
-
"format": "npx prettier --write ."
|
|
10
|
+
"format": "npx prettier --write .",
|
|
11
|
+
"format:check": "npx prettier --check .",
|
|
12
|
+
"test:component": "cypress run --component",
|
|
13
|
+
"release": "release-it"
|
|
11
14
|
},
|
|
12
15
|
"dependencies": {
|
|
13
16
|
"@eox/eslint-config": "^2.0.0",
|
|
14
17
|
"@eox/ui": "^0.3.7",
|
|
15
18
|
"vitepress": "^1.6.3"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@release-it/conventional-changelog": "^10.0.4",
|
|
22
|
+
"@vitejs/plugin-vue": "^6.0.3",
|
|
23
|
+
"cypress": "^15.9.0",
|
|
24
|
+
"release-it": "^19.2.3",
|
|
25
|
+
"vue": "^3.5.26"
|
|
16
26
|
}
|
|
17
27
|
}
|