diff --git a/packages/core/app-layout/docs/page-header.md b/packages/core/app-layout/docs/page-header.md
new file mode 100644
index 0000000000..6557871651
--- /dev/null
+++ b/packages/core/app-layout/docs/page-header.md
@@ -0,0 +1,47 @@
+# PageHeader.vue
+
+A Kong UI dynamic page header component.
+
+- [Features](#features)
+- [Requirements](#requirements)
+- [Usage](#usage)
+ - [Install](#install)
+ - [Props](#props)
+ - [Slots](#slots)
+
+## Features
+
+- Reactive updates based on `prop` value changes :rocket:
+- Slottable areas for displaying custom content, icons, etc.
+
+## Requirements
+
+- `vue` must be initialized in the host application
+- `@kong/kongponents` must be available as a `dependency` in the host application, along with the package's style imports. [See here for instructions on installing Kongponents](https://kongponents.konghq.com/#globally-install-all-kongponents). Specifically, the following Kongponents must be available:
+ - `KBreadcrumb`
+
+## Usage
+
+### Install
+
+[See instructions for installing the `@kong-ui-public/app-layout` package.](../README.md#install)
+
+### Props
+
+#### `title`
+
+- type: `String`
+- required: `false`
+- default: `''`
+
+The title text of the page.
+
+### Slots
+
+#### `center`
+
+The main slot to use for navbar content if you don't need a left/center/right navbar layout.
+
+---
+
+[← Back to `@kong-ui-public/app-layout` docs](../README.md)
diff --git a/packages/core/app-layout/package.json b/packages/core/app-layout/package.json
index ff2c472cb0..a2da840871 100644
--- a/packages/core/app-layout/package.json
+++ b/packages/core/app-layout/package.json
@@ -38,7 +38,7 @@
"test:unit:open": "cross-env FORCE_COLOR=1 vitest --ui"
},
"peerDependencies": {
- "@kong/kongponents": "^8.83.5",
+ "@kong/kongponents": "^8.87.0",
"vue": "^3.2.47",
"vue-router": "^4.2.2"
},
@@ -48,7 +48,7 @@
"lodash.clonedeep": "^4.5.0"
},
"devDependencies": {
- "@kong/kongponents": "^8.83.5",
+ "@kong/kongponents": "^8.87.0",
"@types/lodash.clonedeep": "^4.5.7",
"vue": "^3.2.47",
"vue-router": "^4.2.2"
diff --git a/packages/core/app-layout/sandbox/components/NavLinks.vue b/packages/core/app-layout/sandbox/components/NavLinks.vue
index bf02be2bd5..0248f20bec 100644
--- a/packages/core/app-layout/sandbox/components/NavLinks.vue
+++ b/packages/core/app-layout/sandbox/components/NavLinks.vue
@@ -12,5 +12,8 @@
KM Example
+
+ PageHeader
+
diff --git a/packages/core/app-layout/sandbox/index.ts b/packages/core/app-layout/sandbox/index.ts
index 3ec532d923..c5d79218da 100644
--- a/packages/core/app-layout/sandbox/index.ts
+++ b/packages/core/app-layout/sandbox/index.ts
@@ -28,6 +28,11 @@ const router = createRouter({
name: 'kong-manager-example',
component: () => import('./pages/KongManagerLayoutExample.vue'),
},
+ {
+ path: '/page-header',
+ name: 'page-header',
+ component: () => import('./pages/PageHeader.vue'),
+ },
],
})
diff --git a/packages/core/app-layout/sandbox/pages/PageHeader.vue b/packages/core/app-layout/sandbox/pages/PageHeader.vue
new file mode 100644
index 0000000000..3e41313cbe
--- /dev/null
+++ b/packages/core/app-layout/sandbox/pages/PageHeader.vue
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+ TRUTH
+
+
+
+
+
+
+
+ Do Things
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/core/app-layout/src/components/pageHeader/PageHeader.cy.ts b/packages/core/app-layout/src/components/pageHeader/PageHeader.cy.ts
new file mode 100644
index 0000000000..8471eb03ea
--- /dev/null
+++ b/packages/core/app-layout/src/components/pageHeader/PageHeader.cy.ts
@@ -0,0 +1,74 @@
+// Cypress component test spec file
+
+import PageHeader from './PageHeader.vue'
+
+describe('', () => {
+ it('should correctly render content when using props', () => {
+ const title = 'Cats are Cool'
+ const breadcrumbTitle = 'Home'
+
+ cy.mount(PageHeader, {
+ props: {
+ title,
+ breadcrumbs: [{
+ key: 'home',
+ to: { name: 'home' },
+ text: breadcrumbTitle,
+ icon: 'kong',
+ }],
+ },
+ })
+
+ cy.get('.kong-ui-page-header').should('exist')
+ cy.getTestId('page-breadcrumbs').should('be.visible')
+ cy.get('.k-breadcrumb-text').should('contain.text', breadcrumbTitle)
+ cy.getTestId('page-title-text').should('be.visible')
+ cy.getTestId('page-title-text').should('contain.text', title)
+ })
+
+ it('should correctly render content when use slots', () => {
+ const title = 'Cats are Cool'
+ const breadcrumbText = 'breadcrumbs-are-cool'
+ const iconText = 'title-icons-are-cool'
+ const badgeText = 'title-badges-are-cool'
+ const actionsText = 'actions-are-cool'
+
+ cy.mount(PageHeader, {
+ slots: {
+ breadcrumbs: breadcrumbText,
+ default: title,
+ 'title-icon': iconText,
+ 'title-badge': badgeText,
+ actions: actionsText,
+ },
+ })
+
+ cy.get('.kong-ui-page-header').should('exist')
+ cy.getTestId('page-breadcrumbs').should('be.visible')
+ cy.getTestId('page-breadcrumbs').should('contain.text', breadcrumbText)
+ cy.getTestId('page-title-icon').should('be.visible')
+ cy.getTestId('page-title-icon').should('contain.text', iconText)
+ cy.getTestId('page-title-text').should('be.visible')
+ cy.getTestId('page-title-text').should('contain.text', title)
+ cy.getTestId('page-title-badge').should('be.visible')
+ cy.getTestId('page-title-badge').should('contain.text', badgeText)
+ cy.getTestId('page-actions').should('be.visible')
+ cy.getTestId('page-actions').should('contain.text', actionsText)
+ })
+
+ it('should not render empty slots', () => {
+ const title = 'Cats are Cool'
+
+ cy.mount(PageHeader, {
+ props: {
+ title,
+ },
+ })
+
+ cy.get('.kong-ui-page-header').should('exist')
+ cy.getTestId('page-breadcrumbs').should('not.exist')
+ cy.getTestId('page-title-icon').should('not.exist')
+ cy.getTestId('page-title-badge').should('not.exist')
+ cy.getTestId('page-actions').should('not.exist')
+ })
+})
diff --git a/packages/core/app-layout/src/components/pageHeader/PageHeader.vue b/packages/core/app-layout/src/components/pageHeader/PageHeader.vue
new file mode 100644
index 0000000000..21e76cf2c3
--- /dev/null
+++ b/packages/core/app-layout/src/components/pageHeader/PageHeader.vue
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
diff --git a/packages/core/app-layout/src/index.ts b/packages/core/app-layout/src/index.ts
index a61ae6de82..20a497b010 100644
--- a/packages/core/app-layout/src/index.ts
+++ b/packages/core/app-layout/src/index.ts
@@ -1,11 +1,12 @@
import type { App } from 'vue'
+
+import AccountDropdown from './components/navbar/AccountDropdown.vue'
+import AppError from './components/errors/AppError.vue'
import AppLayout from './components/AppLayout.vue'
import AppNavbar from './components/navbar/AppNavbar.vue'
-import AccountDropdown from './components/navbar/AccountDropdown.vue'
-
import AppSidebar from './components/sidebar/AppSidebar.vue'
+import PageHeader from './components/pageHeader/PageHeader.vue'
import SidebarToggle from './components/sidebar/SidebarToggle.vue'
-import AppError from './components/errors/AppError.vue'
// Export Vue plugin as the default
export default {
@@ -18,12 +19,13 @@ export default {
// Export individual Components
export {
+ AccountDropdown,
+ AppError,
AppLayout,
AppNavbar,
AppSidebar,
+ PageHeader,
SidebarToggle,
- AppError,
- AccountDropdown,
}
export * from './types'
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 4a282d25f1..cbb784fb49 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -257,8 +257,8 @@ importers:
version: 4.5.0
devDependencies:
'@kong/kongponents':
- specifier: ^8.83.5
- version: 8.83.5(vue-router@4.2.2)(vue@3.2.47)
+ specifier: ^8.87.0
+ version: 8.87.0(vue-router@4.2.2)(vue@3.2.47)
'@types/lodash.clonedeep':
specifier: ^4.5.7
version: 4.5.7
@@ -1475,6 +1475,31 @@ packages:
transitivePeerDependencies:
- '@popperjs/core'
- debug
+ dev: true
+
+ /@kong/kongponents@8.87.0(vue-router@4.2.2)(vue@3.2.47):
+ resolution: {integrity: sha512-J0T+qXsqlsKCwOIhPaQhiuSg1XPt0Phw9sT11g4t4U1h1EzYeWPW2hS9KhzQmMNl4d5ex4PXI4ZiUKinwz9DtQ==}
+ engines: {node: '>=16.19.0'}
+ peerDependencies:
+ vue: '>= 3.2.47 < 3.3'
+ vue-router: ^4.1.6
+ dependencies:
+ axios: 0.27.2
+ date-fns: 2.30.0
+ date-fns-tz: 1.3.8(date-fns@2.30.0)
+ focus-trap: 7.4.3
+ focus-trap-vue: 3.4.0(focus-trap@7.4.3)(vue@3.2.47)
+ popper.js: 1.16.1
+ sortablejs: 1.15.0
+ swrv: 1.0.3(vue@3.2.47)
+ uuid: 8.3.2
+ v-calendar: 3.0.3(vue@3.2.47)
+ vue: 3.2.47
+ vue-draggable-next: 2.2.0(sortablejs@1.15.0)(vue@3.2.47)
+ vue-router: 4.2.2(vue@3.2.47)
+ transitivePeerDependencies:
+ - '@popperjs/core'
+ - debug
/@kong/swagger-ui-kong-theme-universal@4.2.5(react-dom@17.0.2)(react@17.0.2)(vue-router@4.2.2)(vue@3.2.47):
resolution: {integrity: sha512-KaGay4ce/LtHvYneNhYtRmzo8TTU47LkJSpkdBMk1UQRrzqPDAAh41KS7dyrQeRU/ZA4qFJMJVsPiz1sXV9FsQ==}
@@ -1482,7 +1507,7 @@ packages:
react: 17.0.2
dependencies:
'@braintree/sanitize-url': 2.1.0
- '@kong/kongponents': 8.83.5(vue-router@4.2.2)(vue@3.2.47)
+ '@kong/kongponents': 8.87.0(vue-router@4.2.2)(vue@3.2.47)
'@kyleshockey/xml': 1.0.2
classnames: 2.3.2
curl-to-har: 1.0.1