Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
af1ebdb
feat(SystemBanner): initial html structure and styles
clukhei Oct 27, 2025
ad046bd
chore(SystemBanner): wip content layout
clukhei Oct 28, 2025
e6f15a4
chore(systembanner): wip
clukhei Nov 5, 2025
de5d406
chore(*): wip
clukhei Nov 6, 2025
71f460b
feat(system-banner): implement animation , pagination looping, varian…
clukhei Nov 10, 2025
16094d7
chore(*): rename component files to SystemBanner
clukhei Nov 11, 2025
26da131
feat(SystemBanner): refactor animation
clukhei Nov 11, 2025
0536553
docs(SystemBanner): add basic docs on storybook
clukhei Nov 12, 2025
950f870
test(SystemBanner): add test cases
clukhei Nov 12, 2025
a1e6c56
chore(*): update branch from master
clukhei Nov 12, 2025
09ecac6
chore(*): bring test utils file over to test folder
clukhei Nov 12, 2025
dfcc8f4
docs(SystemBanner): wip
clukhei Nov 12, 2025
bb5f8aa
chore(*): lint fixes
clukhei Nov 12, 2025
4682142
chore(system banner): update layout for desktop view
Nov 21, 2025
64a14bf
feat(SystemBanner): handle mobile layout
clukhei Nov 21, 2025
ea263f0
feat(SystemBanner): show more when line has clamped
clukhei Nov 21, 2025
fea5cd7
feat(SystemBanner): show more to dynamically appear and disappear whe…
clukhei Nov 24, 2025
c00300d
docs(SystemBanner): wip
clukhei Nov 24, 2025
b3e0430
docs(SystemBanner): complete
clukhei Nov 24, 2025
4fc011f
feat(SystemBanner): pause animation on keyboard focus for better acce…
clukhei Nov 24, 2025
bb218e9
feat(SystemBannerItem): give action div a height to ensure bottom hal…
clukhei Nov 24, 2025
8177ed0
feat(SystemBanner): handle when slot action is not filled
clukhei Dec 5, 2025
1fd095d
feat(SystemBannerItem): hasActionSlot prop for ssr support
clukhei Dec 5, 2025
0def408
docs(SystemBanner): jsdocs refining
clukhei Dec 5, 2025
da5e271
docs(SystemBanner): jsdocs refining
clukhei Dec 5, 2025
048ca6b
Merge branch 'master' into feat/system-banner
clukhei Dec 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions playground/SystemBanner.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<script type="module" src="../src/index.ts"></script>
<link href="../src/themes/day.css" rel="stylesheet" type="text/css" />
<link href="../src/themes/night.css" rel="stylesheet" type="text/css" />
<link href="../src/css/sgds.css" rel="stylesheet" type="text/css" />

<style>
div.another-container {
margin-top: 136px;
transition: margin-top 1s ease;
}
div.another-container.no-banner {
margin-top: 0px;
}
sgds-system-banner {
position: absolute;
inset: 0
}
</style>
<sgds-system-banner show>
<sgds-system-banner-item>
<sgds-icon slot="icon" name="placeholder" size="md"></sgds-icon>
<div>
1 Etiam suscipit nisi eget porta cursus. Ut sit amet felis aliquet, pellentesque mi at, vulputate nunc. Vivamus ac
facilisis tellus. Maecenas ac libero scelerisque tellus maximus accumsan a vehicula arcu. Aenean quis leo gravida,
congue sapien eu, rhoncus Maecenas ac libero scelerisque tellus maximus accumsan a vehicula arcu. Aenean quis leo gravida,
congue sapien eu, rhoncus
</div>
<sgds-link size="sm" variant="light" slot="action"
><a href="#">Action link<sgds-icon name="arrow-right" size="md"></sgds-icon></a
></sgds-link>
</sgds-system-banner-item>
<sgds-system-banner-item>
<sgds-icon slot="icon" name="placeholder" size="md"></sgds-icon>
<div>
2Etiam suscipit nisi eget porta cursus. Ut sit amet felis aliquet, pellentesque mi at, vulputate nunc. Vivamus ac
facilisis tellus. Maecenas ac libero scelerisque tellus maximus accumsan a vehicula arcu. Aenean quis leo gravida,
congue sapien eu, rhoncus
</div>
</sgds-system-banner-item>
<sgds-system-banner-item>
<sgds-icon slot="icon" name="placeholder" size="md"></sgds-icon>
<div>
3Etiam suscipit nisi eget porta cursus. Ut sit amet felis aliquet, pellentesque mi at, vulputate nunc. Vivamus ac
facilisis tellus. Maecenas ac libero scelerisque tellus maximus accumsan a vehicula arcu. Aenean quis leo gravida,
congue sapien eu, rhoncus
</div>
<sgds-link size="sm" variant="light" slot="action"
><a href="#">Action link<sgds-icon name="arrow-right" size="md"></sgds-icon></a
></sgds-link>
</sgds-system-banner-item>
<sgds-system-banner-item>
<sgds-icon slot="icon" name="placeholder" size="md"></sgds-icon>
<div>
4Etiam suscipit nisi eget porta cursus. Ut sit amet felis aliquet, pellentesque mi at, vulputate nunc. Vivamus ac
facilisis tellus. Maecenas ac libero scelerisque tellus maximus accumsan a vehicula arcu. Aenean quis leo gravida,
congue sapien eu, rhoncus
</div>
</sgds-system-banner-item>
<sgds-system-banner-item>
<sgds-icon slot="icon" name="placeholder" size="md"></sgds-icon>
<div>
5Etiam suscipit nisi eget porta cursus. Ut sit amet felis aliquet, pellentesque mi at, vulputate nunc. Vivamus ac
facilisis tellus. Maecenas ac libero scelerisque tellus maximus accumsan a vehicula arcu. Aenean quis leo gravida,
congue sapien eu, rhoncus
</div>
</sgds-system-banner-item>
</sgds-system-banner>

<div class="another-container">another container</div>
<script>
const banner = document.querySelector('sgds-system-banner');
banner.addEventListener("sgds-hide", () => {
console.log("Banner is hidden");
const anotherContainer = document.querySelector('.another-container');
anotherContainer.classList.add('no-banner');
})
</script>
2 changes: 1 addition & 1 deletion scripts/generateStories.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ for (const [key, value] of Object.entries(groupedComponents)) {

// Only add in the ArgType table when there is at least one attribute
const ArgsType = value.map(component =>
component.attributes
component.attributes || component.slots || component.events
? `### ${component.tagName}
<ArgTypes of="${component.tagName}"/>\n
`
Expand Down
2 changes: 1 addition & 1 deletion src/base/button.css
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@
height: var(--sgds-dimension-40);
}
:host([size="xs"]) .btn {
font-size: var(--sgds-font-size-1);
font-size: var(--sgds-font-size-0);
line-height: var(--sgds-line-height-16);
padding: var(--sgds-padding-none) var(--sgds-padding-sm);
min-width: var(--sgds-dimension-64);
Expand Down
11 changes: 11 additions & 0 deletions src/components/SystemBanner/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { SgdsSystemBanner } from "./sgds-system-banner";
import SgdsSystemBannerItem from "./sgds-system-banner-item";

customElements.define("sgds-system-banner", SgdsSystemBanner);
customElements.define("sgds-system-banner-item", SgdsSystemBannerItem);

declare global {
interface HTMLElementTagNameMap {
"sgds-system-banner": SgdsSystemBanner;
}
}
80 changes: 80 additions & 0 deletions src/components/SystemBanner/sgds-system-banner-item.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { html, nothing } from "lit";
import { property, state } from "lit/decorators.js";
import { classMap } from "lit/directives/class-map.js";
import SgdsElement from "../../base/sgds-element";
import alertBannerItemStyles from "./system-banner-item.css";
import { HasSlotController } from "../../utils/slot";

/**
* @slot icon - The slot to pass in an icon element.
* @slot action - The slot to pass in an action element such as a button or link
* @slot default - The slot to pass in the message content of the banner item. Text will be clamped at 2 lines in desktop view and 5 lines in mobile view
*
* @event sgds-show-more - The event emitted when user clicks on "show more" in the banner text message
*/
export class SgdsSystemBannerItem extends SgdsElement {
static styles = [...SgdsElement.styles, alertBannerItemStyles];
/** Used only for SSR to indicate the presence of the `action` slot. */
@property({ type: Boolean }) hasActionSlot = false;

@state() private clamped = false;

private readonly hasSlotController = new HasSlotController(this, "action");

private _resizeObserver: ResizeObserver;
async firstUpdated(_changedProperties) {
super.firstUpdated(_changedProperties);
await this.updateComplete;
this._clampCheck();

// Watch resizing for dynamic layout changes
this._resizeObserver = new ResizeObserver(() => this._clampCheck());
this._resizeObserver.observe(this.shadowRoot.querySelector(".message"));
}

disconnectedCallback() {
super.disconnectedCallback();
if (this._resizeObserver) this._resizeObserver.disconnect();
}
updated() {
if (!this.hasActionSlot) this.hasActionSlot = this.hasSlotController.test("action");
}
private _clampCheck() {
const textEl = this.shadowRoot.querySelector(".message");
requestAnimationFrame(() => {
this.clamped = textEl.scrollHeight > textEl.clientHeight;
});
}

private _handleShowMoreClick() {
this.emit("sgds-show-more");
}
render() {
const banner = this.closest("sgds-system-banner");
return html`
<div class="banner-item">
<slot name="icon"></slot>
<div class="banner-item__message_and__action">
<div class="clamped-container">
<div class=${classMap({ message: true, truncated: this.clamped })}>
<slot></slot>
</div>
${this.clamped
? html`<span class="show-more"
>...<a class="show-more__link" @click="${this._handleShowMoreClick}">show more</a></span
>`
: nothing}
</div>
${this.hasActionSlot || parseInt(banner.getAttribute("data-total-items")) > 1
? html`
<div class="action">
<slot name="action"></slot>
</div>
`
: nothing}
</div>
</div>
`;
}
}
export default SgdsSystemBannerItem;
Loading