diff --git a/packages/web-components/src/components/cbp-app-header/api-docs.mdx b/packages/web-components/src/components/cbp-app-header/api-docs.mdx
index 83aea124..32acaaf2 100644
--- a/packages/web-components/src/components/cbp-app-header/api-docs.mdx
+++ b/packages/web-components/src/components/cbp-app-header/api-docs.mdx
@@ -1,6 +1,9 @@
import { Meta, Markdown } from "@storybook/blocks";
import Docs from './readme.md?raw';
+import NavItemDocs from '../cbp-nav-item/readme.md?raw';
{Docs}
+
+{NavItemDocs}
\ No newline at end of file
diff --git a/packages/web-components/src/components/cbp-app-header/cbp-app-header.scss b/packages/web-components/src/components/cbp-app-header/cbp-app-header.scss
index 717bc2c9..90b5b05b 100644
--- a/packages/web-components/src/components/cbp-app-header/cbp-app-header.scss
+++ b/packages/web-components/src/components/cbp-app-header/cbp-app-header.scss
@@ -2,97 +2,16 @@
/*** Base variables ***
* @Prop --cbp-app-header-color-background: var(--cbp-color-white)
* @Prop --cbp-app-header-color-background-dark: var(--cbp-color-base-neutral-dark);
-
- /*** Child variables ***
- * @Prop --cbp-app-header-child-color: var(--cbp-color-interactive-secondary-darker);
- * @Prop --cbp-app-header-child-color-dark: var(--cbp-color-interactive-secondary-lighter);
- * @Prop --cbp-app-header-child-color-background: transparent;
- * @Prop --cbp-app-header-child-color-background-dark: transparent;
- * @Prop --cbp-app-header-child-color-border: transparent;
- * @Prop --cbp-app-header-child-color-border-dark: transparent;
- * @Prop --cbp-app-header-child-color-outline: var(--cbp-color-white);
- * @Prop --cbp-app-header-child-color-outline-dark: transparent;
-
- /*** Hover variables ***
- * @Prop --cbp-app-header-child-color-text-hover: var(--cbp-color-interactive-secondary-darker);
- * @Prop --cbp-app-header-child-color-text-hover-dark: var(--cbp-color-interactive-secondary-lighter);
- * @Prop --cbp-app-header-child-color-background-hover: var(--cbp-color-interactive-secondary-lighter);
- * @Prop --cbp-app-header-child-color-background-hover-dark: var(--cbp-color-interactive-secondary-darker);
- * @Prop --cbp-app-header-child-color-border-hover: var(--cbp-color-interactive-secondary-darker);
- * @Prop --cbp-app-header-child-color-border-hover-dark: var(--cbp-color-interactive-secondary-lighter);
-
- /*** Focus variables ***
- * @Prop --cbp-app-header-child-color-text-focus: var(--cbp-color-text-lightest);
- * @Prop --cbp-app-header-child-color-text-focus-dark: var(--cbp-color-text-darkest);
- * @Prop --cbp-app-header-child-color-background-focus: var(--cbp-color-interactive-focus-dark);
- * @Prop --cbp-app-header-child-color-background-focus-dark: var(--cbp-color-interactive-focus-light);
- * @Prop --cbp-app-header-child-color-border-focus: var(--cbp-color-interactive-focus-dark);
- * @Prop --cbp-app-header-child-color-border-focus-dark: transparent;
- * @Prop --cbp-app-header-child-color-outline-focus: var(--cbp-color-white);
- * @Prop --cbp-app-header-child-color-outline-focus-dark: var(--cbp-color-black);
-
- /*** Active variables ***
- * @Prop --cbp-app-header-child-color-background-active: var(--cbp-color-interactive-active-dark);
- * @Prop --cbp-app-header-child-color-background-active-dark: var(--cbp-color-interactive-focus-light);
- * @Prop --cbp-app-header-child-color-border-active: var(--cbp-color-interactive-active-dark);
- * @Prop --cbp-app-header-child-color-border-active-dark: var(--cbp-color-text-darkest);
- * */
+*/
:root {
--cbp-app-header-color-background: var(--cbp-color-white);
--cbp-app-header-color-background-dark: var(--cbp-color-base-neutral-dark);
-
- --cbp-app-header-child-color: var(--cbp-color-interactive-secondary-darker);
- --cbp-app-header-child-color-dark: var(--cbp-color-interactive-secondary-lighter);
- --cbp-app-header-child-color-background: transparent;
- --cbp-app-header-child-color-background-dark: transparent;
- --cbp-app-header-child-color-border: transparent;
- --cbp-app-header-child-color-border-dark: transparent;
- --cbp-app-header-child-color-outline: var(--cbp-color-white);
- --cbp-app-header-child-color-outline-dark: transparent;
-
- --cbp-app-header-child-color-text-hover: var(--cbp-color-interactive-secondary-darker);
- --cbp-app-header-child-color-text-hover-dark: var(--cbp-color-interactive-secondary-lighter);
- --cbp-app-header-child-color-background-hover: var(--cbp-color-interactive-secondary-lighter);
- --cbp-app-header-child-color-background-hover-dark: var(--cbp-color-interactive-secondary-darker);
- --cbp-app-header-child-color-border-hover: var(--cbp-color-interactive-secondary-darker);
- --cbp-app-header-child-color-border-hover-dark: var(--cbp-color-interactive-secondary-lighter);
-
- --cbp-app-header-child-color-text-focus: var(--cbp-color-text-lightest);
- --cbp-app-header-child-color-text-focus-dark: var(--cbp-color-text-darkest);
- --cbp-app-header-child-color-background-focus: var(--cbp-color-interactive-focus-dark);
- --cbp-app-header-child-color-background-focus-dark: var(--cbp-color-interactive-focus-light);
- --cbp-app-header-child-color-border-focus: var(--cbp-color-interactive-focus-dark);
- --cbp-app-header-child-color-border-focus-dark: transparent;
- --cbp-app-header-child-color-outline-focus: var(--cbp-color-white);
- --cbp-app-header-child-color-outline-focus-dark: var(--cbp-color-black);
-
- --cbp-app-header-child-color-background-active: var(--cbp-color-interactive-active-dark);
- --cbp-app-header-child-color-background-active-dark: var(--cbp-color-interactive-focus-light);
- --cbp-app-header-child-color-border-active: var(--cbp-color-interactive-active-dark);
- --cbp-app-header-child-color-border-active-dark: var(--cbp-color-text-darkest);
}
[data-cbp-theme=light] cbp-app-header[context*=dark]:not([context=light-always]),
[data-cbp-theme=dark] cbp-app-header:not([context=dark-inverts]):not([context=light-always]) {
--cbp-app-header-color-background: var(--cbp-app-header-color-background-dark);
-
- --cbp-app-header-child-color: var(--cbp-app-header-child-color-dark);
- --cbp-app-header-child-color-background: var(--cbp-app-header-child-color-background-dark);
- --cbp-app-header-child-color-border: var(--cbp-app-header-child-color-border-dark);
- --cbp-app-header-child-color-outline: var(--cbp-app-header-child-color-outline-dark);
-
- --cbp-app-header-child-color-text-hover: var(--cbp-app-header-child-color-text-hover-dark);
- --cbp-app-header-child-color-background-hover: var(--cbp-app-header-child-color-background-hover-dark);
- --cbp-app-header-child-color-border-hover: var(--cbp-app-header-child-color-border-hover-dark);
-
- --cbp-app-header-child-color-text-focus: var(--cbp-app-header-child-color-text-focus-dark);
- --cbp-app-header-child-color-background-focus: var(--cbp-app-header-child-color-background-focus-dark);
- --cbp-app-header-child-color-border-focus: var(--cbp-app-header-child-color-border-focus-dark);
- --cbp-app-header-child-color-outline-focus: var(--cbp-app-header-child-color-outline-focus-dark);
-
- --cbp-app-header-child-color-background-active: var(--cbp-app-header-child-color-background-active-dark);
- --cbp-app-header-child-color-border-active: var(--cbp-app-header-child-color-border-active-dark);
}
cbp-app-header {
@@ -108,60 +27,4 @@ cbp-app-header {
box-shadow: var(--cbp-shadow-level-3-down);
z-index: var(--cbp-z-index-level-1);
- // Style all nav items consistently, whether links or button controls for menu/dropdown.
- // This may need to be refactored or pulled out and placed into a new component, e.g., cbp-nav-item.
- // Actually rendering them as cbp-button components would cut down on the redundant CSS and support most cases as drawer controls and links.
- //.nav-home,
- //.cbp-nav-item,
- //.cbp-menu-dropdown,
- button, a {
- display: flex;
- align-items: center;
- height: 100%;
- min-height: var(--cbp-space-14x);
- padding: 0 var(--cbp-space-3x);
- color: var(--cbp-app-header-child-color);
- background-color: var(--cbp-app-header-child-color-background);
- font-size: var(--cbp-font-size-heading-xs);
- font-weight: var(--cbp-font-weight-medium);
- text-decoration: none;
- white-space: nowrap;
- border-color: var(--cbp-app-header-child-color-border);
- border-style: solid;
- border-width: 0 0 var(--cbp-border-size-xl) 0;
- outline-color: var(--cbp-app-header-child-color-outline);
- outline-style: solid;
- outline-width: 0;
- outline-offset: calc(-1 * var(--cbp-space-1x));
- cursor: pointer;
-
- &:hover {
- color: var(--cbp-app-header-child-color-text-hover);
- background-color: var(--cbp-app-header-child-color-background-hover);
- border-color: var(--cbp-app-header-child-color-border-hover);
- }
-
- &:focus {
- color: var(--cbp-app-header-child-color-text-focus);
- background-color: var(--cbp-app-header-child-color-background-focus);
- border-color: var(--cbp-app-header-child-color-border-focus);
- outline-width: var(--cbp-border-size-md);
- outline-color: var(--cbp-app-header-child-color-outline-focus);
-
- }
-
- &:active {
- background-color: var(--cbp-app-header-child-color-background-active);
- border: var(--cbp-app-header-child-color-border-active);
- }
- }
-
- [slot=cbp-home] {
- font-size: var(--cbp-font-size-heading-sm);
- font-weight: var(--cbp-font-weight-bold);
- padding-left: var(--cbp-space-5x);
- padding-right: var(--cbp-space-5x);
- margin-left: calc(-1 * var(--cbp-space-5x));
- }
-
}
diff --git a/packages/web-components/src/components/cbp-app-header/cbp-app-header.stories.tsx b/packages/web-components/src/components/cbp-app-header/cbp-app-header.stories.tsx
index f50a1054..6f14efe0 100644
--- a/packages/web-components/src/components/cbp-app-header/cbp-app-header.stories.tsx
+++ b/packages/web-components/src/components/cbp-app-header/cbp-app-header.stories.tsx
@@ -5,10 +5,6 @@ export default {
layout: 'fullscreen',
},
argTypes: {
- context : {
- control: 'select',
- options: [ "light-inverts", "light-always", "dark-inverts", "dark-always"]
- },
sx: {
description: 'Supports adding inline styles as an object of key-value pairs comprised of CSS properties and values. Values should reference design tokens when possible.',
control: 'object',
@@ -16,15 +12,60 @@ export default {
},
};
-const Template = ({ context, sx }) => {
+function generateNavItems(navItems){
+ const html = navItems.map(({html, selected}) => {
+ return ` ${html}`;
+ }
+ );
+ return html.join('');
+}
+
+
+const Template = ({ navItems, sx }) => {
return `
- Application Name
+ ${generateNavItems(navItems)}
`;
};
export const ApplicationHeader = Template.bind({});
+
+ApplicationHeader.args = {
+ navItems: [
+ {
+ html: `
+ Application Name
+ `,
+ selected: true
+ },{
+ html: `
+ Single Nav Item 1
+ `,
+ selected: false,
+ },
+ {
+ html: `
+ Single Nav Item 2
+ `,
+ selected: false
+ },
+ ]
+};
\ No newline at end of file
diff --git a/packages/web-components/src/components/cbp-app-header/cbp-app-header.tsx b/packages/web-components/src/components/cbp-app-header/cbp-app-header.tsx
index 7b56918e..851c15b6 100644
--- a/packages/web-components/src/components/cbp-app-header/cbp-app-header.tsx
+++ b/packages/web-components/src/components/cbp-app-header/cbp-app-header.tsx
@@ -1,4 +1,4 @@
-import { Component, Host, h, Prop } from '@stencil/core';
+import { Component, Element, Host, h } from '@stencil/core';
@Component({
tag: 'cbp-app-header',
@@ -7,8 +7,44 @@ import { Component, Host, h, Prop } from '@stencil/core';
export class CbpAppHeader {
- /** Specifies the context of the component as it applies to the visual design and whether it inverts when light/dark mode is toggled. Default behavior is "light-inverts" and does not have to be specified. */
- @Prop({ reflect: true }) context: "light-inverts" | "light-always" | "dark-inverts" | "dark-always";
+ private navItems: HTMLCbpNavItemElement[] = [];
+
+ @Element() host: HTMLElement;
+
+ initNavItemset() {
+ // check for a default navItem, otherwise set the first one active
+ let activeNavItem;
+ activeNavItem = this.host.querySelector('cbp-nav-item[selected]');
+
+ this.setActiveNav(activeNavItem);
+ }
+
+ setActiveNav(activatedNav) {
+ this.navItems.forEach((navItem: HTMLCbpNavItemElement) => {
+ let link = navItem.querySelector('a, button');
+
+ if (activatedNav == navItem){
+ navItem.selected = true;
+ link.setAttribute('aria-current', 'true');
+ } else {
+ navItem.selected = false;
+ link.removeAttribute('aria-current');
+ }
+ })
+ }
+
+ componentWillLoad() {
+ this.navItems = Array.from(this.host.querySelectorAll('cbp-nav-item'));
+
+ // Attach event listeners to the child navItem
+ this.navItems.forEach(navItem => {
+ navItem.addEventListener('navClicked', e => this.setActiveNav(e.detail.host));
+ });
+ }
+
+ componentDidLoad() {
+ this.initNavItemset();
+ }
render() {
return (
@@ -19,3 +55,4 @@ export class CbpAppHeader {
);
}
}
+
diff --git a/packages/web-components/src/components/cbp-nav-item/cbp-nav-item.scss b/packages/web-components/src/components/cbp-nav-item/cbp-nav-item.scss
new file mode 100644
index 00000000..fdd632df
--- /dev/null
+++ b/packages/web-components/src/components/cbp-nav-item/cbp-nav-item.scss
@@ -0,0 +1,126 @@
+/**
+ * @Prop --cbp-nav-item-color: var(--cbp-color-interactive-secondary-darker);
+ * @Prop --cbp-nav-item-color-dark: var(--cbp-color-interactive-secondary-lighter);
+
+ * @Prop --cbp-nav-item-color-hover: var(--cbp-color-interactive-secondary-darker);
+ * @Prop --cbp-nav-item-color-hover-dark: var(--cbp-color-interactive-secondary-lighter);
+ * @Prop --cbp-nav-item-color-bg-hover: var(--cbp-color-interactive-secondary-lighter);
+ * @Prop --cbp-nav-item-color-bg-hover-dark: var(--cbp-color-interactive-secondary-darker);
+ * @Prop --cbp-nav-item-border-bottom-hover: var(--cbp-color-interactive-secondary-darker);
+ * @Prop --cbp-nav-item-border-bottom-hover-dark: var(--cbp-color-interactive-secondary-lighter);
+
+ * @Prop --cbp-nav-item-color-active: var(--cbp-color-text-lightest);
+ * @Prop --cbp-nav-item-color-active-dark: var(--cbp-color-text-darkest);
+ * @Prop --cbp-nav-item-color-bg-active: var(--cbp-color-interactive-active-dark);
+ * @Prop --cbp-nav-item-color-bg-active-dark: var(--cbp-color-interactive-active-light);
+
+ * @Prop --cbp-nav-item-color-selected: var(--cbp-color-interactive-active-dark);
+ * @Prop --cbp-nav-item-color-selected-dark: var(--cbp-color-interactive-active-light);
+ * @Prop --cbp-nav-item-color-bg-selected: var(transparent);
+ * @Prop --cbp-nav-item-border-bottom-selected: var(--cbp-color-interactive-active-dark);
+ * @Prop --cbp-nav-item-border-bottom-selected-dark: var(--cbp-color-interactive-active-light);
+
+ */
+
+ :root {
+ --cbp-nav-item-color: var(--cbp-color-interactive-secondary-darker);
+ --cbp-nav-item-color-dark: var(--cbp-color-interactive-secondary-lighter);
+
+ --cbp-nav-item-color-hover: var(--cbp-color-interactive-secondary-darker);
+ --cbp-nav-item-color-hover-dark: var(--cbp-color-interactive-secondary-lighter);
+ --cbp-nav-item-color-bg-hover: var(--cbp-color-interactive-secondary-lighter);
+ --cbp-nav-item-color-bg-hover-dark: var(--cbp-color-interactive-secondary-darker);
+ --cbp-nav-item-border-bottom-hover: var(--cbp-color-interactive-secondary-darker);
+ --cbp-nav-item-border-bottom-hover-dark: var(--cbp-color-interactive-secondary-lighter);
+
+ --cbp-nav-item-color-active: var(--cbp-color-text-lightest);
+ --cbp-nav-item-color-active-dark: var(--cbp-color-text-darkest);
+ --cbp-nav-item-color-bg-active: var(--cbp-color-interactive-active-dark);
+ --cbp-nav-item-color-bg-active-dark: var(--cbp-color-interactive-active-light);
+
+ --cbp-nav-item-color-selected: var(--cbp-color-interactive-active-dark);
+ --cbp-nav-item-color-selected-dark: var(--cbp-color-interactive-active-light);
+ --cbp-nav-item-color-bg-selected: var(transparent);
+ --cbp-nav-item-border-bottom-selected: var(--cbp-color-interactive-active-dark);
+ --cbp-nav-item-border-bottom-selected-dark: var(--cbp-color-interactive-active-light);
+}
+
+/*
+* Dark Mode - display dark design based on mode or context.
+*/
+
+[data-cbp-theme=light] cbp-app-header:not(#fakeId),
+[data-cbp-theme=dark] cbp-app-header:not(#fakeId) {
+
+--cbp-nav-item-color: var(--cbp-nav-item-color-dark);
+--cbp-nav-item-color-hover: var(--cbp-nav-item-color-hover-dark);
+
+--cbp-nav-item-color-bg-hover: var(--cbp-nav-item-color-bg-hover-dark);
+--cbp-nav-item-border-bottom-hover: var(--cbp-nav-item-border-bottom-hover-dark);
+
+--cbp-nav-item-color-active: var(--cbp-nav-item-color-active-dark);
+--cbp-nav-item-color-bg-active: var(--cbp-nav-item-color-bg-active-dark);
+
+--cbp-nav-item-color-selected: var(--cbp-nav-item-color-selected-dark);
+--cbp-nav-item-border-bottom-selected: var(--cbp-nav-item-border-bottom-selected-dark);
+}
+
+
+cbp-nav-item {
+
+/* remap cbp-button vars */
+cbp-button[fill=ghost][color=secondary] {
+ --cbp-button-min-height: 3.5rem;
+ --cbp-button-color: var(--cbp-nav-item-color);
+ --cbp-button-border-width: 0 0 var(--cbp-border-size-xl) 0;
+ --cbp-button-border-radius: var(--cbp-border-radius-sharp);
+
+ font-size: var(--cbp-font-weight-bold);
+
+ button, a{
+ text-transform: unset;
+ }
+
+ &:hover{
+ --cbp-button-color-hover: var(--cbp-nav-item-color-hover);
+ --cbp-button-color-bg-hover: var(--cbp-nav-item-color-bg-hover);
+
+ //tech debt: cbp-button dark overrides causing issues due to specificity, assigned the nav-item border colors to the --cbp-button-color-border-hover & --cbp-button-color-border-hover-dark seperately to circumvent issue
+ --cbp-button-color-border-hover: var(--cbp-nav-item-border-bottom-hover);
+ --cbp-button-color-border-hover-dark: var(--cbp-nav-item-border-bottom-hover-dark);
+ }
+
+ &:active{
+ --cbp-button-color: var(--cbp-nav-item-color-active);
+ --cbp-button-color-bg: var(--cbp-nav-item-color-bg-active);
+
+ //tech debt: cbp-button dark overrides causing issues due to specificity, assigned the nav-item border colors to the --cbp-button-color-border-active & --cbp-button-color-border-active-dark seperately to circumvent issue
+ --cbp-button-color-dark: var(--cbp-nav-item-color-active-dark);
+ --cbp-button-color-bg-dark: var(--cbp-nav-item-color-bg-active-dark);
+ }
+}
+
+//Selected/Active state
+&[selected] cbp-button[fill=ghost][color=secondary]{
+ --cbp-button-color: var(--cbp-nav-item-color-selected);
+ --cbp-button-color-bg: var(--cbp-nav-item-color-bg-selected);
+ --cbp-button-color-border: var(--cbp-nav-item-border-bottom-selected);
+ //TODO: need to set text to italics (confirm with Jer)
+ font-style: italic;
+ //tech debt: cbp-button dark overrides causing issues due to specificity, same issue as with hover & active above
+ --cbp-button-color-dark: var(--cbp-nav-item-color-selected-dark);
+ --cbp-button-color-bg-dark: var(--cbp-nav-item-color-bg-selected-dark);
+ --cbp-button-color-border-dark: var(--cbp-nav-item-border-bottom-selected-dark);
+
+}
+
+//styling for home/application title nav item
+&:first-of-type a{
+ font-size: var(--cbp-font-size-heading-sm);
+ font-weight: var(--cbp-font-weight-bold);
+ padding-left: var(--cbp-space-5x);
+ padding-right: var(--cbp-space-5x);
+ margin-left: calc(-1 * var(--cbp-space-5x));
+ letter-spacing: unset;
+}
+}
\ No newline at end of file
diff --git a/packages/web-components/src/components/cbp-nav-item/cbp-nav-item.tsx b/packages/web-components/src/components/cbp-nav-item/cbp-nav-item.tsx
new file mode 100644
index 00000000..c14b88dc
--- /dev/null
+++ b/packages/web-components/src/components/cbp-nav-item/cbp-nav-item.tsx
@@ -0,0 +1,44 @@
+import { Component, Element, Event, EventEmitter, Host, Prop, h } from '@stencil/core';
+import { setCSSProps } from '../../utils/utils';
+@Component({
+ tag: 'cbp-nav-item',
+ styleUrl: 'cbp-nav-item.scss',
+})
+
+export class CbpNavItem {
+
+ @Element() host: HTMLElement;
+
+ /** Specifies whether this is the selected nav-item. Only one item per set should be marked as selected.*/
+ @Prop({ reflect: true }) selected: boolean;
+
+ /** Supports adding inline styles as an object */
+ @Prop() sx: any = {};
+
+ @Event() navClicked: EventEmitter;
+ handleNavClick() {
+ this.selected=true;
+ this.navClicked.emit({
+ host: this.host,
+ })
+ }
+
+ componentWillLoad() {
+ if (typeof this.sx == 'string') {
+ this.sx = JSON.parse(this.sx) || {};
+ }
+ setCSSProps(this.host, {
+ ...this.sx,
+ });
+ }
+
+ render() {
+ return (
+ this.handleNavClick()}
+ >
+
+
+ );
+ }
+}