From 535bc9809292035234fde1617019d22cf1f24882 Mon Sep 17 00:00:00 2001 From: MariaIDineva Date: Mon, 5 Jan 2026 11:05:48 +0200 Subject: [PATCH 1/2] docs(ui5): add fiori wizard --- .../docs/ui5-webcomponents-fiori/api-files.ts | 3 +- .../ui5-webcomponents-fiori/docs-data.json | 4 + .../ui5-webcomponents-fiori/docs-routes.ts | 4 + .../wizard/examples/basic-sample.html | 228 +++++++++++++++ .../wizard/examples/basic-sample.ts | 266 ++++++++++++++++++ .../wizard/header/wizard-header.html | 9 + .../wizard/header/wizard-header.ts | 19 ++ .../ui5-webcomponents-fiori/wizard/index.ts | 19 ++ .../wizard/project.json | 9 + .../wizard/tsconfig.json | 18 ++ .../wizard/wizard-docs.html | 10 + .../wizard/wizard-docs.ts | 42 +++ 12 files changed, 630 insertions(+), 1 deletion(-) create mode 100644 libs/docs/ui5-webcomponents-fiori/wizard/examples/basic-sample.html create mode 100644 libs/docs/ui5-webcomponents-fiori/wizard/examples/basic-sample.ts create mode 100644 libs/docs/ui5-webcomponents-fiori/wizard/header/wizard-header.html create mode 100644 libs/docs/ui5-webcomponents-fiori/wizard/header/wizard-header.ts create mode 100644 libs/docs/ui5-webcomponents-fiori/wizard/index.ts create mode 100644 libs/docs/ui5-webcomponents-fiori/wizard/project.json create mode 100644 libs/docs/ui5-webcomponents-fiori/wizard/tsconfig.json create mode 100644 libs/docs/ui5-webcomponents-fiori/wizard/wizard-docs.html create mode 100644 libs/docs/ui5-webcomponents-fiori/wizard/wizard-docs.ts diff --git a/libs/docs/ui5-webcomponents-fiori/api-files.ts b/libs/docs/ui5-webcomponents-fiori/api-files.ts index 9e102cd22ed..ce5c969adab 100644 --- a/libs/docs/ui5-webcomponents-fiori/api-files.ts +++ b/libs/docs/ui5-webcomponents-fiori/api-files.ts @@ -24,5 +24,6 @@ export const API_FILES = { uploadCollection: ['UploadCollection', 'UploadCollectionItem'], userMenu: ['UserMenu', 'UserMenuAccount', 'UserMenuItem', 'UserMenuItemGroup'], userSettingsDialog: ['UserSettingsAccountView', 'UserSettingsDialog', 'UserSettingsItem', 'UserSettingsView'], - viewSettingsDialog: ['FilterItem', 'FilterItemOption', 'GroupItem', 'SortItem', 'ViewSettingsDialog'] + viewSettingsDialog: ['FilterItem', 'FilterItemOption', 'GroupItem', 'SortItem', 'ViewSettingsDialog'], + wizard: ['Wizard', 'WizardStep'] }; diff --git a/libs/docs/ui5-webcomponents-fiori/docs-data.json b/libs/docs/ui5-webcomponents-fiori/docs-data.json index ce182eac29c..a47ecf03734 100644 --- a/libs/docs/ui5-webcomponents-fiori/docs-data.json +++ b/libs/docs/ui5-webcomponents-fiori/docs-data.json @@ -77,6 +77,10 @@ { "url": "ui5-webcomponents-fiori/view-settings-dialog", "name": "View Settings Dialog" + }, + { + "url": "ui5-webcomponents-fiori/wizard", + "name": "Wizard" } ] } diff --git a/libs/docs/ui5-webcomponents-fiori/docs-routes.ts b/libs/docs/ui5-webcomponents-fiori/docs-routes.ts index 7740967d2e9..a6638690b59 100644 --- a/libs/docs/ui5-webcomponents-fiori/docs-routes.ts +++ b/libs/docs/ui5-webcomponents-fiori/docs-routes.ts @@ -102,6 +102,10 @@ const componentRoutes = [ path: 'view-settings-dialog', loadChildren: () => import('@fundamental-ngx/docs/ui5-webcomponents-fiori/view-settings-dialog').then(configureLibRoutes) + }, + { + path: 'wizard', + loadChildren: () => import('@fundamental-ngx/docs/ui5-webcomponents-fiori/wizard').then(configureLibRoutes) } ]; diff --git a/libs/docs/ui5-webcomponents-fiori/wizard/examples/basic-sample.html b/libs/docs/ui5-webcomponents-fiori/wizard/examples/basic-sample.html new file mode 100644 index 00000000000..27b03c1a794 --- /dev/null +++ b/libs/docs/ui5-webcomponents-fiori/wizard/examples/basic-sample.html @@ -0,0 +1,228 @@ +
+ + @for (step of steps(); track $index) { + +
+ @if ($index === 0) { + + {{ step.titleText }} + Please provide information about your product. + + @if (!step1Valid() && productName().length > 0) { + + Please fill in all required fields to continue. + + } + +
+
+ Product Name + +
+ +
+ Product Description + +
+
+ } @if ($index === 1) { + + {{ step.titleText }} + Select a pricing plan for your product. + +
+
+ + + +
+
+ } @if ($index === 2) { + + {{ step.titleText }} + Choose your preferred delivery method and additional options. + + @if (!step3Valid()) { + + Please select a delivery method. + + } + +
+ + + +
+ } @if ($index === 3) { + + {{ step.titleText }} + Provide your contact information for order updates. + + @if (!step4Valid() && customerName().length > 0) { + + Please fill in all required fields with valid information. + + } + +
+
+ Full Name + +
+ +
+ Email Address + +
+ +
+ Phone Number + +
+
+ } @if ($index === 4) { + + {{ step.titleText }} + Review your information before submitting. + + + All steps completed! Please review your information below. + + +
+ Product Information +
+ Product Name: + {{ productName() }} +
+
+ Description: + {{ productDescription() }} +
+
+ +
+ Pricing +
+ Selected Plan: + + @if (selectedPrice() === 'basic') { Basic - $9.99/month } @else if (selectedPrice() === + 'professional') { Professional - $29.99/month } @else { Enterprise - $99.99/month } + +
+
+ +
+ Delivery Options +
+ Delivery Method: + + @if (standardDelivery()) { Standard Delivery (Free) } @else if (expressDelivery()) { Express + Delivery ($15.00) } + +
+ @if (giftWrap()) { +
+ Additional: + Gift Wrap ($5.00) +
+ } +
+ +
+ Contact Information +
+ Name: + {{ customerName() }} +
+
+ Email: + {{ customerEmail() }} +
+ @if (customerPhone()) { +
+ Phone: + {{ customerPhone() }} +
+ } +
+ } + + + +
+
+ } +
+
diff --git a/libs/docs/ui5-webcomponents-fiori/wizard/examples/basic-sample.ts b/libs/docs/ui5-webcomponents-fiori/wizard/examples/basic-sample.ts new file mode 100644 index 00000000000..d3f8d0c7ade --- /dev/null +++ b/libs/docs/ui5-webcomponents-fiori/wizard/examples/basic-sample.ts @@ -0,0 +1,266 @@ +import { Component, computed, signal } from '@angular/core'; +import type { UI5WrapperCustomEvent } from '@fundamental-ngx/ui5-webcomponents-base/types'; +import { Wizard } from '@fundamental-ngx/ui5-webcomponents-fiori/wizard'; +import { WizardStep } from '@fundamental-ngx/ui5-webcomponents-fiori/wizard-step'; +import { Button } from '@fundamental-ngx/ui5-webcomponents/button'; +import { CheckBox } from '@fundamental-ngx/ui5-webcomponents/check-box'; +import { Input } from '@fundamental-ngx/ui5-webcomponents/input'; +import { Label } from '@fundamental-ngx/ui5-webcomponents/label'; +import { MessageStrip } from '@fundamental-ngx/ui5-webcomponents/message-strip'; +import { RadioButton } from '@fundamental-ngx/ui5-webcomponents/radio-button'; +import { Text } from '@fundamental-ngx/ui5-webcomponents/text'; +import { TextArea } from '@fundamental-ngx/ui5-webcomponents/text-area'; +import { Title } from '@fundamental-ngx/ui5-webcomponents/title'; + +// Import icons +import '@ui5/webcomponents-icons/dist/accept.js'; +import '@ui5/webcomponents-icons/dist/cart.js'; +import '@ui5/webcomponents-icons/dist/employee.js'; +import '@ui5/webcomponents-icons/dist/palette.js'; +import '@ui5/webcomponents-icons/dist/product.js'; + +interface WizardStepData { + titleText: string; + subtitleText?: string; + icon: string; + completed: boolean; + valid: boolean; +} + +@Component({ + selector: 'ui5-doc-wizard-basic-sample', + templateUrl: './basic-sample.html', + standalone: true, + imports: [Wizard, WizardStep, Button, Input, Label, Title, Text, TextArea, CheckBox, RadioButton, MessageStrip], + styles: [ + ` + .wizard-container { + width: 100%; + height: 600px; + } + .step-content { + padding: 2rem; + } + .form-row { + display: flex; + flex-direction: column; + gap: 0.5rem; + margin-bottom: 1.5rem; + } + .form-group { + display: flex; + flex-direction: column; + gap: 1rem; + } + .radio-group { + display: flex; + flex-direction: column; + gap: 0.75rem; + margin-top: 0.5rem; + } + .navigation-buttons { + display: flex; + gap: 1rem; + margin-top: 2rem; + padding-top: 1rem; + border-top: 1px solid var(--sapGroup_ContentBorderColor, #d9d9d9); + } + .summary-section { + margin-bottom: 1.5rem; + } + .summary-item { + display: flex; + gap: 0.5rem; + margin-bottom: 0.5rem; + } + .summary-label { + font-weight: bold; + min-width: 150px; + } + ` + ] +}) +export class BasicSample { + // Current step index + currentStep = signal(0); + + // Step 1: Product Information + productName = signal(''); + productDescription = signal(''); + + // Step 2: Pricing + selectedPrice = signal('basic'); + + // Step 3: Delivery Options + standardDelivery = signal(true); + expressDelivery = signal(false); + giftWrap = signal(false); + + // Step 4: Contact Information + customerName = signal(''); + customerEmail = signal(''); + customerPhone = signal(''); + + // Computed: Step validation + step1Valid = computed(() => this.productName().trim().length > 0 && this.productDescription().trim().length > 0); + + step2Valid = computed(() => this.selectedPrice().length > 0); + + step3Valid = computed(() => this.standardDelivery() || this.expressDelivery()); + + step4Valid = computed(() => { + const emailValid = this.customerEmail().includes('@'); + return this.customerName().trim().length > 0 && emailValid; + }); + + // Steps configuration + steps = computed(() => [ + { + titleText: 'Product Information', + subtitleText: 'Enter product details', + icon: 'product', + completed: this.currentStep() > 0, + valid: this.step1Valid() + }, + { + titleText: 'Pricing', + subtitleText: 'Select pricing plan', + icon: 'palette', + completed: this.currentStep() > 1, + valid: this.step2Valid() + }, + { + titleText: 'Delivery Options', + subtitleText: 'Choose delivery method', + icon: 'cart', + completed: this.currentStep() > 2, + valid: this.step3Valid() + }, + { + titleText: 'Contact Information', + subtitleText: 'Provide contact details', + icon: 'employee', + completed: this.currentStep() > 3, + valid: this.step4Valid() + }, + { + titleText: 'Summary', + subtitleText: 'Review and confirm', + icon: 'accept', + completed: false, + valid: true + } + ]); + + // Computed: Can navigate to next step + canGoNext = computed(() => { + switch (this.currentStep()) { + case 0: + return this.step1Valid(); + case 1: + return this.step2Valid(); + case 2: + return this.step3Valid(); + case 3: + return this.step4Valid(); + default: + return false; + } + }); + + // Computed: Can navigate to previous step + canGoPrevious = computed(() => this.currentStep() > 0); + + // Computed: Can submit wizard + canSubmit = computed( + () => this.step1Valid() && this.step2Valid() && this.step3Valid() && this.step4Valid() && this.isLastStep() + ); + + // Computed: Is last step + isLastStep = computed(() => this.currentStep() === this.steps().length - 1); + + onStepChange(event: UI5WrapperCustomEvent): void { + const detail = event.detail; + if (detail && detail.step) { + const stepElement = detail.step; + const stepIndex = Array.from(stepElement.parentElement?.children || []).indexOf(stepElement); + if (stepIndex !== -1) { + this.currentStep.set(stepIndex); + } + } + } + + goToNextStep(): void { + if (this.canGoNext() && !this.isLastStep()) { + this.currentStep.update((current) => Math.min(current + 1, this.steps().length - 1)); + } + } + + goToPreviousStep(): void { + if (this.canGoPrevious()) { + this.currentStep.update((current) => Math.max(current - 1, 0)); + } + } + + submitWizard(): void { + if (this.isLastStep()) { + alert('Wizard completed successfully!'); + this.resetWizard(); + } + } + + resetWizard(): void { + this.currentStep.set(0); + this.productName.set(''); + this.productDescription.set(''); + this.selectedPrice.set('basic'); + this.standardDelivery.set(true); + this.expressDelivery.set(false); + this.giftWrap.set(false); + this.customerName.set(''); + this.customerEmail.set(''); + this.customerPhone.set(''); + } + + onProductNameChange(event: UI5WrapperCustomEvent): void { + this.productName.set(event.currentTarget.value); + } + + onProductDescriptionChange(event: UI5WrapperCustomEvent): void { + this.productDescription.set(event.currentTarget.value); + } + + onCustomerNameChange(event: UI5WrapperCustomEvent): void { + this.customerName.set(event.currentTarget.value); + } + + onCustomerEmailChange(event: UI5WrapperCustomEvent): void { + this.customerEmail.set(event.currentTarget.value); + } + + onCustomerPhoneChange(event: UI5WrapperCustomEvent): void { + this.customerPhone.set(event.currentTarget.value); + } + + onPriceChange(price: string): void { + this.selectedPrice.set(price); + } + + onStandardDeliveryChange(event: UI5WrapperCustomEvent): void { + this.standardDelivery.set(event.currentTarget.checked); + if (event.currentTarget.checked) { + this.expressDelivery.set(false); + } + } + + onExpressDeliveryChange(event: UI5WrapperCustomEvent): void { + this.expressDelivery.set(event.currentTarget.checked); + if (event.currentTarget.checked) { + this.standardDelivery.set(false); + } + } + + onGiftWrapChange(event: UI5WrapperCustomEvent): void { + this.giftWrap.set(event.currentTarget.checked); + } +} diff --git a/libs/docs/ui5-webcomponents-fiori/wizard/header/wizard-header.html b/libs/docs/ui5-webcomponents-fiori/wizard/header/wizard-header.html new file mode 100644 index 00000000000..6678ef400c4 --- /dev/null +++ b/libs/docs/ui5-webcomponents-fiori/wizard/header/wizard-header.html @@ -0,0 +1,9 @@ + +
{{componentName}}
+ + + + + + +
diff --git a/libs/docs/ui5-webcomponents-fiori/wizard/header/wizard-header.ts b/libs/docs/ui5-webcomponents-fiori/wizard/header/wizard-header.ts new file mode 100644 index 00000000000..4c63eac3ca5 --- /dev/null +++ b/libs/docs/ui5-webcomponents-fiori/wizard/header/wizard-header.ts @@ -0,0 +1,19 @@ +import { Component } from '@angular/core'; +import { + DescriptionComponent, + DocPageComponent, + HeaderComponent, + HeaderTabsComponent, + ImportComponent +} from '@fundamental-ngx/docs/shared'; + +@Component({ + selector: 'ui5-wizard-header', + templateUrl: './wizard-header.html', + standalone: true, + imports: [DocPageComponent, HeaderComponent, DescriptionComponent, ImportComponent, HeaderTabsComponent] +}) +export class WizardHeader { + componentName = 'Wizard'; + packageName = '@ui5/webcomponents-fiori'; +} diff --git a/libs/docs/ui5-webcomponents-fiori/wizard/index.ts b/libs/docs/ui5-webcomponents-fiori/wizard/index.ts new file mode 100644 index 00000000000..5943042ff8c --- /dev/null +++ b/libs/docs/ui5-webcomponents-fiori/wizard/index.ts @@ -0,0 +1,19 @@ +import { Routes } from '@angular/router'; +import { WizardHeader } from './header/wizard-header'; +import { WizardDocs } from './wizard-docs'; + +export const ROUTES: Routes = [ + { + path: '', + component: WizardHeader, + data: { primary: true }, + children: [ + { + path: '', + component: WizardDocs + } + ] + } +]; +export const LIBRARY_NAME = 'wizard'; +export const API_FILE_KEY = 'wizard'; diff --git a/libs/docs/ui5-webcomponents-fiori/wizard/project.json b/libs/docs/ui5-webcomponents-fiori/wizard/project.json new file mode 100644 index 00000000000..f6c38bf02fc --- /dev/null +++ b/libs/docs/ui5-webcomponents-fiori/wizard/project.json @@ -0,0 +1,9 @@ +{ + "name": "docs-ui5-webcomponents-fiori-wizard", + "$schema": "../../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/docs/ui5-webcomponents-fiori/wizard", + "prefix": "ui5-doc", + "projectType": "library", + "tags": ["type:lib", "scope:docs"], + "targets": {} +} diff --git a/libs/docs/ui5-webcomponents-fiori/wizard/tsconfig.json b/libs/docs/ui5-webcomponents-fiori/wizard/tsconfig.json new file mode 100644 index 00000000000..26b852b9336 --- /dev/null +++ b/libs/docs/ui5-webcomponents-fiori/wizard/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../../../tsconfig.base.json", + "files": [], + "include": ["**/*.ts"], + "compilerOptions": { + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitReturns": false, + "noFallthroughCasesInSwitch": true, + "target": "es2020", + "outDir": "../../../../dist/out-tsc", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [] + }, + "exclude": ["**/*.test.ts", "**/*.spec.ts", "**/*.e2e-spec.ts"] +} diff --git a/libs/docs/ui5-webcomponents-fiori/wizard/wizard-docs.html b/libs/docs/ui5-webcomponents-fiori/wizard/wizard-docs.html new file mode 100644 index 00000000000..26af1c8c97c --- /dev/null +++ b/libs/docs/ui5-webcomponents-fiori/wizard/wizard-docs.html @@ -0,0 +1,10 @@ + Basic Wizard Example + + The Wizard component guides users through a multi-step process. Each step can have a title, subtitle, and icon. + Users can navigate between steps by clicking the step headers or using the navigation buttons. The example below + demonstrates a product configuration wizard with validation and step completion tracking. + + + + + diff --git a/libs/docs/ui5-webcomponents-fiori/wizard/wizard-docs.ts b/libs/docs/ui5-webcomponents-fiori/wizard/wizard-docs.ts new file mode 100644 index 00000000000..2b1872355ae --- /dev/null +++ b/libs/docs/ui5-webcomponents-fiori/wizard/wizard-docs.ts @@ -0,0 +1,42 @@ +import { Component } from '@angular/core'; +import { + CodeExampleComponent, + ComponentExampleComponent, + DescriptionComponent, + DocsSectionTitleComponent, + ExampleFile, + getAssetFromModuleAssets +} from '@fundamental-ngx/docs/shared'; +import { BasicSample } from './examples/basic-sample'; + +const basicSampleTs = 'basic-sample.ts'; +const basicSampleHtml = 'basic-sample.html'; + +@Component({ + selector: 'ui5-wizard-docs', + templateUrl: './wizard-docs.html', + standalone: true, + imports: [ + CodeExampleComponent, + ComponentExampleComponent, + DescriptionComponent, + DocsSectionTitleComponent, + BasicSample + ] +}) +export class WizardDocs { + basicExample: ExampleFile[] = [ + { + language: 'typescript', + fileName: 'basic-sample', + code: getAssetFromModuleAssets(basicSampleTs), + component: 'BasicSample' + }, + { + language: 'html', + fileName: 'basic-sample', + code: getAssetFromModuleAssets(basicSampleHtml), + scssFileCode: getAssetFromModuleAssets(basicSampleHtml) + } + ]; +} From 2a62bc61dab569b7b471f0287fcab8064f55822d Mon Sep 17 00:00:00 2001 From: MariaIDineva Date: Mon, 5 Jan 2026 14:48:03 +0200 Subject: [PATCH 2/2] docs(ui5): use fundamental styles --- .../wizard/examples/basic-sample.html | 131 +++++++++--------- .../wizard/examples/basic-sample.ts | 53 +------ 2 files changed, 74 insertions(+), 110 deletions(-) diff --git a/libs/docs/ui5-webcomponents-fiori/wizard/examples/basic-sample.html b/libs/docs/ui5-webcomponents-fiori/wizard/examples/basic-sample.html index 27b03c1a794..5584311fe9e 100644 --- a/libs/docs/ui5-webcomponents-fiori/wizard/examples/basic-sample.html +++ b/libs/docs/ui5-webcomponents-fiori/wizard/examples/basic-sample.html @@ -1,4 +1,4 @@ -
+
@for (step of steps(); track $index) { -
+
@if ($index === 0) { {{ step.titleText }} Please provide information about your product. @if (!step1Valid() && productName().length > 0) { - + Please fill in all required fields to continue. } -
-
- Product Name +
+
+ Product Name
-
- Product Description +
+ Product Description {{ step.titleText }} Select a pricing plan for your product. -
-
- - - -
+
+ + +
} @if ($index === 2) { @@ -72,21 +72,23 @@ Choose your preferred delivery method and additional options. @if (!step3Valid()) { - + Please select a delivery method. } -
+
Provide your contact information for order updates. @if (!step4Valid() && customerName().length > 0) { - + Please fill in all required fields with valid information. } -
-
- Full Name +
+
+ Full Name
-
- Email Address +
+ Email Address
-
- Phone Number +
+ Phone Number All steps completed! Please review your information below. -
+
Product Information -
- Product Name: +
+ Product Name: {{ productName() }}
-
- Description: +
+ Description: {{ productDescription() }}
-
+
Pricing -
- Selected Plan: +
+ Selected Plan: @if (selectedPrice() === 'basic') { Basic - $9.99/month } @else if (selectedPrice() === 'professional') { Professional - $29.99/month } @else { Enterprise - $99.99/month } @@ -171,36 +173,36 @@
-
+
Delivery Options -
- Delivery Method: +
+ Delivery Method: @if (standardDelivery()) { Standard Delivery (Free) } @else if (expressDelivery()) { Express Delivery ($15.00) }
@if (giftWrap()) { -
- Additional: +
+ Additional: Gift Wrap ($5.00)
}
-
+
Contact Information -
- Name: +
+ Name: {{ customerName() }}
-
- Email: +
+ Email: {{ customerEmail() }}
@if (customerPhone()) { -
- Phone: +
+ Phone: {{ customerPhone() }}
} @@ -208,8 +210,13 @@ } -