+
+
Text + Suffix
+
+
+
+ Confirm
+
+
+
+ Add
+
+
+
+ Remove
+
+
+
+
+
Text + Suffix + Loading
+
+
+
+ Confirm
+
+
+
+ Add
+
+
+
+
+
Icon + Text + Suffix
+
+
+
+
+ Action
+
+
+
+
+ Delete
+
+
+
+
+
Icon + Text + Suffix + Loading
+
+
+
+
+ Action
+
+
+
+
+ Delete
+
+
+
+
+
Icon + Suffix + Loading (no text)
+
+
+
+
+
+
+
+
+
+
+
+
+
Suffix Only
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Suffix Only + Loading + Circle
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+ }),
+}
+
export const Loading: Story = {
render: () => ({
components: { OsButton, IconCheck },
diff --git a/packages/ui/src/components/OsButton/OsButton.visual.spec.ts b/packages/ui/src/components/OsButton/OsButton.visual.spec.ts
index d5caa00ea..1de82bb79 100644
--- a/packages/ui/src/components/OsButton/OsButton.visual.spec.ts
+++ b/packages/ui/src/components/OsButton/OsButton.visual.spec.ts
@@ -227,6 +227,26 @@ test.describe('OsButton visual regression', () => {
await checkA11y(page)
})
+ test('suffix', async ({ page }) => {
+ await page.goto(`${STORY_URL}--suffix&viewMode=story`)
+ const root = page.locator(STORY_ROOT)
+ await root.waitFor()
+ await waitForFonts(page)
+ // Pause animations for deterministic screenshots
+ await page.evaluate(() => {
+ document.querySelectorAll('.os-button__spinner').forEach((el) => {
+ ;(el as HTMLElement).style.animationPlayState = 'paused'
+ })
+ document.querySelectorAll('.os-button__spinner circle').forEach((el) => {
+ ;(el as HTMLElement).style.animationPlayState = 'paused'
+ })
+ })
+
+ await expect(root.locator('.flex-col').first()).toHaveScreenshot('suffix.png')
+
+ await checkA11y(page)
+ })
+
test('loading', async ({ page }) => {
await page.goto(`${STORY_URL}--loading&viewMode=story`)
const root = page.locator(STORY_ROOT)
diff --git a/packages/ui/src/components/OsButton/OsButton.vue b/packages/ui/src/components/OsButton/OsButton.vue
index 8a47072a5..e85c268bd 100644
--- a/packages/ui/src/components/OsButton/OsButton.vue
+++ b/packages/ui/src/components/OsButton/OsButton.vue
@@ -21,6 +21,7 @@
*
* @slot default - Button content (text or HTML)
* @slot icon - Optional icon (rendered left of text). Use aria-label for icon-only buttons.
+ * @slot suffix - Optional trailing content (rendered right of text). Icons, badges, chevrons etc.
*/
type Size = NonNullable