From 16646fff40767c2661c2ebca8a27e4a5212a6ebd Mon Sep 17 00:00:00 2001 From: Radoslav Karaivanov Date: Wed, 11 Mar 2026 13:41:12 +0200 Subject: [PATCH] chore: More enhancements to stories --- stories/calendar.stories.ts | 6 + stories/card.stories.ts | 351 +++++++++++++++++++-------- stories/carousel.stories.ts | 285 ++++++++++++++++------ stories/checkbox.stories.ts | 113 +++++++++ stories/chip.stories.ts | 207 ++++++++++++++-- stories/circular-progress.stories.ts | 325 ++++++++++++++++++++++--- stories/combo.stories.ts | 133 ++++++++++ stories/date-range-picker.stories.ts | 74 ++++++ stories/date-time-input.stories.ts | 144 +++++++++++ stories/datepicker.stories.ts | 95 ++++++++ stories/dialog.stories.ts | 94 ++++--- stories/divider.stories.ts | 112 ++++++--- stories/dropdown.stories.ts | 44 +++- stories/expansion-panel.stories.ts | 53 ++-- stories/file-input.stories.ts | 70 +++--- stories/icon-button.stories.ts | 100 ++++---- stories/linear-progress.stories.ts | 164 +++++++++++-- stories/list.stories.ts | 170 +++++++++++-- stories/mask-input.stories.ts | 82 +++++++ stories/nav-drawer.stories.ts | 123 +++++----- stories/navbar.stories.ts | 120 ++++++--- stories/rating.stories.ts | 115 +++++---- 22 files changed, 2382 insertions(+), 598 deletions(-) diff --git a/stories/calendar.stories.ts b/stories/calendar.stories.ts index 9d8c0d54cb..235669f4f9 100644 --- a/stories/calendar.stories.ts +++ b/stories/calendar.stories.ts @@ -8,6 +8,7 @@ import { IgcCalendarComponent, defineComponents, } from 'igniteui-webcomponents'; +import { disableStoryControls } from './story.js'; defineComponents(IgcCalendarComponent); @@ -232,6 +233,7 @@ export const Basic: Story = { }; export const RangeSelection: Story = { + argTypes: disableStoryControls(metadata), render: () => { const start = new Date(currentYear, currentMonth, 5); const end = new Date(currentYear, currentMonth, 18); @@ -246,6 +248,7 @@ export const RangeSelection: Story = { }; export const MultipleSelection: Story = { + argTypes: disableStoryControls(metadata), render: () => { const values = [ new Date(currentYear, currentMonth, 3), @@ -264,6 +267,7 @@ export const MultipleSelection: Story = { }; export const MultipleMonths: Story = { + argTypes: disableStoryControls(metadata), render: () => { const start = new Date(currentYear, currentMonth, 20); const end = new Date(currentYear, currentMonth + 1, 10); @@ -279,6 +283,7 @@ export const MultipleMonths: Story = { }; export const DisabledDates: Story = { + argTypes: disableStoryControls(metadata), render: () => { const disabledDates: DateRangeDescriptor[] = [ { type: DateRangeType.Weekends }, @@ -297,6 +302,7 @@ export const DisabledDates: Story = { }; export const SpecialDates: Story = { + argTypes: disableStoryControls(metadata), render: () => { const specialDates: DateRangeDescriptor[] = [ { diff --git a/stories/card.stories.ts b/stories/card.stories.ts index 649bc83b06..347247a898 100644 --- a/stories/card.stories.ts +++ b/stories/card.stories.ts @@ -14,6 +14,7 @@ import { defineComponents, registerIconFromText, } from 'igniteui-webcomponents'; +import { disableStoryControls } from './story.js'; defineComponents( IgcAvatarComponent, @@ -65,141 +66,301 @@ type Story = StoryObj; // endregion -const Template = ({ elevated = false }: IgcCardArgs) => { - return html` -
-
- +const cityImageUrl = + 'https://images.unsplash.com/photo-1518235506717-e1ed3306a89b?ixlib=rb-1.2.1&auto=format&fit=crop&w=640&q=80'; +const avatarUrl = + 'https://www.infragistics.com/angular-demos/assets/images/men/1.jpg'; +const albumCoverUrl = + 'https://www.infragistics.com/angular-demos/assets/images/card/media/here_media.jpg'; + +const cardStyle = 'max-width: 344px;'; +const rowStyle = 'display: flex; flex-wrap: wrap; gap: 1.5rem; padding: 1rem;'; + +export const Basic: Story = { + render: ({ elevated }) => html` +
+ + + New York City + + + +

New York

+
City that never sleeps
+
+ +

+ New York City comprises 5 boroughs sitting where the Hudson River + meets the Atlantic Ocean. At its core is Manhattan, a densely + populated borough that's among the world's major commercial, + financial and cultural centers. +

+
+ + Like + Learn More + + + +
+
+ `, +}; + +export const Outlined: Story = { + argTypes: disableStoryControls(metadata), + parameters: { + docs: { + description: { + story: + 'The card has two visual styles: **outlined** (default) renders with a border, and **elevated** renders with a drop shadow.', + }, + }, + }, + render: () => html` +
+
+

Outlined (default)

+ - + New York City + + +

New York

+
City that never sleeps
+
+ +

Outlined cards use a subtle border to define their boundary.

+
+ + Learn More + +
+
+
+

Elevated

+ + + New York City + + +

New York

+
City that never sleeps
+
+ +

Elevated cards use a drop shadow to lift them off the page.

+
+ + Learn More + +
+
+
+ `, +}; + +export const MediaPosition: Story = { + argTypes: disableStoryControls(metadata), + parameters: { + docs: { + description: { + story: + 'The `igc-card-media` component can be placed before or after `igc-card-header` to control where the image appears relative to the card title.', + }, + }, + }, + render: () => html` +
+
+

+ Media before header +

+ + + New York City - -

Title

-
Subtitle
+ src=${avatarUrl} + > +

New York

+
City that never sleeps
-

- New York City comprises 5 boroughs sitting where the Hudson River - meets the Atlantic Ocean. At its core is Manhattan, a densely - populated borough that’s among the world’s major commercial, - financial and cultural centers. -

+

Media placed above the header creates a visually rich card.

Like - Learn More - +
-
- +
+

Media after header

+ - -

Title

-
Subtitle
+ src=${avatarUrl} + > +

New York

+
City that never sleeps
- + New York City -

- New York City comprises 5 boroughs sitting where the Hudson River - meets the Atlantic Ocean. At its core is Manhattan, a densely - populated borough that's among the world's major commercial, - financial and cultural centers. -

+

Media placed below the header leads with the title context.

- Like - Learn More - - + Like +
-
-
-
- -
+ `, +}; + +export const Horizontal: Story = { + argTypes: disableStoryControls(metadata), + parameters: { + docs: { + description: { + story: + 'Cards can be laid out horizontally by applying `flex-direction: row` to the card element, useful for media-beside-text patterns.', + }, + }, + }, + render: () => html` +
+ +
+ + +

Rozes

+
Under the Grave (2016)
+
+ + I write what's real and what's true, even if it means throwing + myself under the bus. + +
+ + + + + +
+ +
+
- -

Rozes

-
Under the Grave (2016)
+ src=${albumCoverUrl} + > +

HERE

+
By Mellow D
- As I have always said: I write what's real and what's true, even - if it means throwing myself under the bus. +

+ Far far away, behind the word mountains, far from the countries + Vokalia and Consonantia, there live the blind texts. +

+ + Play Album +
- - - - + + Album cover + +
+
+
+ `, +}; + +export const ActionsOrientation: Story = { + argTypes: disableStoryControls(metadata), + parameters: { + docs: { + description: { + story: + 'The `igc-card-actions` component supports `horizontal` (default) and `vertical` orientations for laying out action buttons.', + }, + }, + }, + render: () => html` +
+
+

+ Horizontal actions (default) +

+ + +

Horizontal

+
Actions spread across the bottom
+
+ +

+ Actions are laid out in a row with start-aligned buttons and + end-aligned icon buttons. +

+
+ + Like + Share + +
-
- -
-
- - - -

HERE

-
By Mellow D
-
- -

- Far far away, behind the word mountains, far from the - countries Vokalia and Consonantia, there live the blind texts. -

-
- - PLAY ALBUM - -
- - - -
+
+

Vertical actions

+ + +

Vertical

+
Actions stacked on the side
+
+ + + + +
- `; + `, }; - -export const Basic: Story = Template.bind({}); diff --git a/stories/carousel.stories.ts b/stories/carousel.stories.ts index 7fd4ca9148..841c83ae57 100644 --- a/stories/carousel.stories.ts +++ b/stories/carousel.stories.ts @@ -10,6 +10,7 @@ import { defineComponents, registerIconFromText, } from 'igniteui-webcomponents'; +import { disableStoryControls } from './story.js'; defineComponents( IgcCarouselComponent, @@ -228,6 +229,14 @@ const fancyImages = [ ]; export const Basic: Story = { + parameters: { + docs: { + description: { + story: + 'A standard image carousel with default dot indicators and navigation arrows. Use the controls panel to interactively explore the available properties.', + }, + }, + }, render: (args) => html` html` + + ${defaultImages.map( + ({ src, alt }) => html` + + ${alt} + + ` + )} + + `, +}; + +export const AnimationTypes: Story = { + argTypes: disableStoryControls(metadata), + parameters: { + docs: { + description: { + story: + 'The `animationType` property controls the transition effect between slides. The three available modes are **slide** (default), **fade**, and **none** (instant switch).', + }, + }, + }, + render: () => html` + +
+
+

slide (default)

+ + ${defaultImages.map( + ({ src, alt }) => html` + + ${alt} + + ` + )} + +
+
+

fade

+ + ${defaultImages.map( + ({ src, alt }) => html` + + ${alt} + + ` + )} + +
+
+

none

+ + ${defaultImages.map( + ({ src, alt }) => html` + + ${alt} + + ` + )} + +
+
+ `, +}; + +export const AutoPlay: Story = { + parameters: { + docs: { + description: { + story: + 'Setting the `interval` property (in milliseconds) enables automatic slide advancement. By default the carousel pauses on user interaction; enable `disable-pause-on-interaction` to keep it playing continuously. Use the **Play** and **Pause** buttons below to control playback programmatically.', + }, + }, + }, + args: { + interval: 2000, + }, render: (args) => html` + ${defaultImages.map( + ({ src, alt }) => html` + + ${alt} + + ` + )} + +
+ Play + Pause +
+ `, +}; + +export const CustomNavigation: Story = { + argTypes: disableStoryControls(metadata), + parameters: { + docs: { + description: { + story: + 'Custom navigation icons and indicator styles can be provided via the `previous-button` and `next-button` named slots. Per-slide indicators can be replaced with `igc-carousel-indicator` elements; the `active` slot determines the appearance of the currently active indicator.', + }, + }, + }, + render: () => html` + html` +export const ThumbnailIndicators: Story = { + argTypes: disableStoryControls(metadata), + parameters: { + docs: { + description: { + story: + 'Thumbnail images can be used as indicator controls by pairing each `igc-carousel-slide` with an `igc-carousel-indicator`. The `active` slot determines the appearance of the selected indicator.', + }, + }, + }, + render: () => html` + + + ${fancyImages.map( + ({ src, alt }) => html` + + ${alt} + + + + ${`${alt} + ${`${alt} + + ` + )} + + `, +}; + +export const Multipage: Story = { + argTypes: disableStoryControls(metadata), + parameters: { + docs: { + description: { + story: + 'Carousel slides can host any content including interactive form elements. This example shows a two-step sign-up flow spanning two slides.', + }, + }, + }, + render: () => html` - + 🐱 @@ -346,7 +535,7 @@ export const InputsTemplate: Story = { Comment
Not a member? πŸ™€ - Sign up + Sign up
@@ -363,67 +552,9 @@ export const InputsTemplate: Story = { Sign up
Already a member? - Comment + Comment
`, }; - -export const ThumbnailTemplate: Story = { - render: (args) => html` - - - ${fancyImages.map( - ({ src, alt }) => html` - - ${alt} - - - - ${`${alt} - ${`${alt} - - ` - )} - - `, -}; diff --git a/stories/checkbox.stories.ts b/stories/checkbox.stories.ts index 5612fad601..5189fc350f 100644 --- a/stories/checkbox.stories.ts +++ b/stories/checkbox.stories.ts @@ -108,6 +108,14 @@ type Story = StoryObj; // endregion export const Default: Story = { + parameters: { + docs: { + description: { + story: + 'A basic checkbox illustrating all available properties. Use the controls panel to explore available properties interactively.', + }, + }, + }, render: (args) => html` { + const onParentChange = (e: Event) => { + const parent = e.target as IgcCheckboxComponent; + document + .querySelectorAll('.child-cb') + .forEach((cb) => { + cb.checked = parent.checked; + }); + }; + const onChildChange = () => { + const children = [ + ...document.querySelectorAll('.child-cb'), + ]; + const checkedCount = children.filter((cb) => cb.checked).length; + const parent = + document.querySelector('#parent-cb')!; + parent.indeterminate = checkedCount > 0 && checkedCount < children.length; + parent.checked = checkedCount === children.length; + }; + return html` +
+ + Select all + +
+ + Option 1 + + + Option 2 + + + Option 3 + +
+
+ `; + }, +}; + +export const LabelPosition: Story = { + argTypes: disableStoryControls(metadata), + parameters: { + docs: { + description: { + story: + "The `labelPosition` property controls whether the label appears before or after the checkbox indicator. Defaults to `'after'`.", + }, + }, + }, + render: () => html` +
+ Label after (default) + Label before +
+ `, +}; + +export const States: Story = { + argTypes: disableStoryControls(metadata), + parameters: { + docs: { + description: { + story: + 'Visual overview of all checkbox states: unchecked, checked, indeterminate, disabled, disabled checked, invalid, and checked invalid.', + }, + }, + }, + render: () => html` +
+ Unchecked + Checked + Indeterminate + Disabled + Checked & Disabled + Invalid + Checked & Invalid +
+ `, +}; + export const Form: Story = { argTypes: disableStoryControls(metadata), + parameters: { + docs: { + description: { + story: + 'Demonstrates checkbox behaviour inside an HTML `
` element, including required validation, indeterminate state, initial checked state, and disabled fieldsets.', + }, + }, + }, render: () => { return html` diff --git a/stories/chip.stories.ts b/stories/chip.stories.ts index 9efe579f42..92a5693ca5 100644 --- a/stories/chip.stories.ts +++ b/stories/chip.stories.ts @@ -3,6 +3,7 @@ import { html } from 'lit'; import { ifDefined } from 'lit/directives/if-defined.js'; import { IgcChipComponent, defineComponents } from 'igniteui-webcomponents'; +import { disableStoryControls } from './story.js'; defineComponents(IgcChipComponent); @@ -86,24 +87,188 @@ type Story = StoryObj; // endregion -const ChipTemplate = ({ - disabled, - removable, - selectable, - selected, - variant, -}: IgcChipArgs) => html` - - 😱 - Chip - πŸ‘€ - -`; - -export const Basic: Story = ChipTemplate.bind({}); +export const Basic: Story = { + parameters: { + docs: { + description: { + story: + 'A basic chip illustrating all available properties. Use the controls panel to explore the available properties interactively.', + }, + }, + }, + render: ({ + disabled, + removable, + selectable, + selected, + variant, + }: IgcChipArgs) => html` + + 😱 + Chip + πŸ‘€ + + `, +}; + +export const Variants: Story = { + argTypes: disableStoryControls(metadata), + parameters: { + docs: { + description: { + story: + 'The `variant` property applies a semantic color to the chip. Available values are **primary**, **info**, **success**, **warning**, and **danger**. Omitting the property renders the chip in its default neutral style.', + }, + }, + }, + render: () => html` +
+ Default + Primary + Info + Success + Warning + Danger +
+ `, +}; + +export const Selectable: Story = { + argTypes: disableStoryControls(metadata), + parameters: { + docs: { + description: { + story: + 'Setting `selectable` allows a chip to be toggled. The `igcSelect` event fires on each toggle. A common pattern is using selectable chips as filter tags β€” the example below tracks the active filters and updates a summary.', + }, + }, + }, + render: () => { + const filters = ['All', 'Music', 'Art', 'Sport', 'Food', 'Travel']; + const selected = new Set(['All']); + + const onSelect = (e: CustomEvent) => { + const chip = e.target as IgcChipComponent; + const label = chip.textContent!.trim(); + if (e.detail) { + selected.add(label); + } else { + selected.delete(label); + } + const summary = document.querySelector('#filter-summary')!; + summary.textContent = + selected.size === 0 + ? 'No filters active' + : `Active filters: ${[...selected].join(', ')}`; + }; + + return html` +
+
+ ${filters.map( + (label) => html` + + ${label} + + ` + )} +
+

+ Active filters: All +

+
+ `; + }, +}; + +export const Removable: Story = { + argTypes: disableStoryControls(metadata), + parameters: { + docs: { + description: { + story: + 'Setting `removable` renders a dismiss button on the chip. The `igcRemove` event fires when the button is clicked. The example below removes the chip from the DOM on dismissal.', + }, + }, + }, + render: () => { + const tags = ['Angular', 'React', 'Vue', 'Svelte', 'Lit']; + + const onRemove = (e: Event) => { + (e.target as HTMLElement).remove(); + }; + + return html` +
+ ${tags.map( + (tag) => html` + ${tag} + ` + )} +
+ `; + }, +}; + +export const PrefixSuffix: Story = { + argTypes: disableStoryControls(metadata), + parameters: { + docs: { + description: { + story: + 'Chips support `prefix` and `suffix` slots for placing icons or decorative elements on either side of the label.', + }, + }, + }, + render: () => html` +
+ + πŸ“ + Prefix only + + + Suffix only + πŸ”– + + + 🎡 + Both slots + β–Ά + +
+ `, +}; + +export const States: Story = { + argTypes: disableStoryControls(metadata), + parameters: { + docs: { + description: { + story: + 'Visual overview of chip states: default, selected, disabled, disabled+selected, removable, and selectable.', + }, + }, + }, + render: () => html` +
+ Default + Selected + Disabled + Disabled & Selected + Removable + Selectable +
+ `, +}; diff --git a/stories/circular-progress.stories.ts b/stories/circular-progress.stories.ts index 1b211a0d51..6e8784f1b7 100644 --- a/stories/circular-progress.stories.ts +++ b/stories/circular-progress.stories.ts @@ -6,6 +6,7 @@ import { IgcCircularProgressComponent, defineComponents, } from 'igniteui-webcomponents'; +import { disableStoryControls } from './story.js'; defineComponents(IgcCircularProgressComponent); @@ -103,16 +104,25 @@ type Story = StoryObj; // endregion -const Template = ({ - variant, - hideLabel, - value, - max, - animationDuration, - indeterminate, - labelFormat, -}: IgcCircularProgressArgs) => html` -
+export const Basic: Story = { + parameters: { + docs: { + description: { + story: + 'A basic circular progress indicator. Use the controls panel to explore all available properties interactively.', + }, + }, + }, + args: { value: 60 }, + render: ({ + variant, + hideLabel, + value, + max, + animationDuration, + indeterminate, + labelFormat, + }: IgcCircularProgressArgs) => html` - html` +
- - - - - - - SVG - -
Label
-
-
-`; +
+ + Primary +
+
+ + Info +
+
+ + Success +
+
+ + Warning +
+
+ + Danger +
+
+ `, +}; -export const Basic: Story = Template.bind({}); +export const Indeterminate: Story = { + argTypes: disableStoryControls(metadata), + parameters: { + docs: { + description: { + story: + 'Setting `indeterminate` switches the indicator into an infinite spin animation, used when the duration of an operation is unknown.', + }, + }, + }, + render: () => html` +
+
+ + Primary +
+
+ + Success +
+
+ + Danger +
+
+ `, +}; + +export const GradientTrack: Story = { + argTypes: disableStoryControls(metadata), + parameters: { + docs: { + description: { + story: + 'Custom gradient stops can be slotted via `igc-circular-gradient` elements with the `gradient` slot. Each stop accepts an `offset` percentage and a `color`. The default label slot can also be replaced with arbitrary content.', + }, + }, + }, + render: () => html` +
+ + + + + + + + + 50% + + + + + Done + +
+ `, +}; + +export const CustomSize: Story = { + argTypes: disableStoryControls(metadata), + parameters: { + docs: { + description: { + story: + 'The diameter and stroke thickness of the ring are controlled via the `--diameter` and `--stroke-thickness` CSS custom properties, making it easy to embed the indicator at any scale.', + }, + }, + }, + render: () => html` +
+
+ + Default +
+
+ + Medium +
+
+ + Large +
+
+ `, +}; + +export const LabelFormat: Story = { + argTypes: disableStoryControls(metadata), + parameters: { + docs: { + description: { + story: + 'The `labelFormat` property allows customising the text displayed inside the ring. Use `{0}` for the current value and `{1}` for the max value. Setting `hideLabel` removes the label entirely.', + }, + }, + }, + render: () => html` + +
+
+ + Default +
+
+ + {value}/{max} +
+
+ + Step {value} +
+
+ + Hidden +
+
+ `, +}; diff --git a/stories/combo.stories.ts b/stories/combo.stories.ts index d02799eee8..b30707ce1e 100644 --- a/stories/combo.stories.ts +++ b/stories/combo.stories.ts @@ -307,6 +307,14 @@ registerIconFromText( ); export const Default: Story = { + parameters: { + docs: { + description: { + story: + 'A fully-featured combo with grouped city data, a custom item template, a prefix icon, and helper text. Use the controls panel to explore all available properties interactively.', + }, + }, + }, args: { label: 'Location(s)', placeholder: 'Cities of interest', @@ -350,6 +358,14 @@ export const Default: Story = { export const NoData: Story = { argTypes: disableStoryControls(metadata), + parameters: { + docs: { + description: { + story: + 'When no data is bound the combo renders an empty-state area. The default template shows a generic message; slot `empty` accepts arbitrary content for a fully custom empty state.', + }, + }, + }, render: () => html`