Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions .changeset/empty-coins-dig.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@swisspost/design-system-styles': major
'@swisspost/design-system-components': minor
'@swisspost/design-system-documentation': patch
---

Removed the HTML stepper component and replaced it with new `post-stepper` and `post-stepper-item` web components.
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,18 @@ <h2>Post Tooltip</h2>
<button class="btn btn-secondary btn-large">Button</button>
</post-tooltip-trigger>
</div>

<div class="my-24">
<h2>Post Stepper</h2>
<post-stepper
completedLabel="Completed step"
currentLabel="Current step"
stepLabel="Step"
[currentIndex]="1"
>
<post-stepper-item> Step 1 </post-stepper-item>
<post-stepper-item> Step 2 </post-stepper-item>
<post-stepper-item> Step 3 </post-stepper-item>
<post-stepper-item> Step 4 </post-stepper-item>
</post-stepper>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
PostTabHeader,
PostTabPanel,
PostTooltipTrigger,
PostStepper,
PostStepperItem,
} from 'components';

@Component({
Expand All @@ -39,9 +41,10 @@ import {
PostTabHeader,
PostTabPanel,
PostTooltipTrigger,
]
PostStepper,
PostStepperItem,
],
})

export class HomeComponent {
isCollapsed = false;
}
188 changes: 188 additions & 0 deletions packages/components/cypress/e2e/stepper.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
describe('stepper', { baseUrl: null }, () => {
beforeEach(() => {
cy.visit('./cypress/fixtures/post-stepper.test.html');
});

afterEach(() => {
cy.reload();
});

it('should render the post-stepper component', () => {
cy.get('post-stepper').should('exist');
});

// Errors

it('should log an error if the current label is missing', () => {
cy.window().then(win => {
cy.spy(win.console, 'error').as('consoleError');
});
cy.get('post-stepper').invoke('attr', 'current-label', null);
cy.get('@consoleError').should('be.called');
});

it('should log an error if the completed label is missing', () => {
cy.window().then(win => {
cy.spy(win.console, 'error').as('consoleError');
});
cy.get('post-stepper').invoke('attr', 'completed-label', null);
cy.get('@consoleError').should('be.called');
});

it('should log an error if the mobile step label is missing', () => {
cy.window().then(win => {
cy.spy(win.console, 'error').as('consoleError');
});
cy.get('post-stepper').invoke('attr', 'step-label', null);
cy.get('@consoleError').should('be.called');
});

// Dynamic classes

it('should set all inactive classes when current-index is negative', () => {
cy.get('post-stepper')
.invoke('attr', 'current-index', -1)
.wait(100)
.find('post-stepper-item')
.then($items => {
expect($items.filter('.stepper-item-inactive').length).to.equal(5);
});
});

it('should set first step as active when current-index is 0', () => {
cy.get('post-stepper')
.invoke('attr', 'current-index', 0)
.wait(100)
.find('post-stepper-item')
.then($items => {
expect($items[0]).to.have.class('stepper-item-current');
expect($items.filter('.stepper-item-inactive').length).to.equal(4);
});
});

it('should set third step as active when current-index is 2', () => {
cy.get('post-stepper')
.invoke('attr', 'current-index', 2)
.wait(100)
.find('post-stepper-item')
.then($items => {
expect($items[2]).to.have.class('stepper-item-current');
expect($items.filter('.stepper-item-completed').length).to.equal(2);
expect($items.filter('.stepper-item-inactive').length).to.equal(2);
});
});

it('should set all steps as complete but the last when current-index is last element', () => {
cy.get('post-stepper')
.invoke('attr', 'current-index', 4)
.wait(100)
.find('post-stepper-item')
.then($items => {
expect($items[4]).to.have.class('stepper-item-current');
expect($items.filter('.stepper-item-completed').length).to.equal(4);
expect($items.filter('.stepper-item-inactive').length).to.equal(0);
});
});

it('should set all steps as complete when current-index is bigger than the steps length', () => {
cy.get('post-stepper')
.invoke('attr', 'current-index', 5)
.wait(100)
.find('post-stepper-item')
.then($items => {
expect($items.filter('.stepper-item-current').length).to.equal(0);
expect($items.filter('.stepper-item-completed').length).to.equal(5);
expect($items.filter('.stepper-item-inactive').length).to.equal(0);
});
});

// Accessibility and labels

it('should set aria-current="step" on the current step', () => {
cy.get('post-stepper')
.invoke('attr', 'current-index', 1)
.wait(100)
.find('post-stepper-item')
.eq(1)
.should('have.attr', 'aria-current', 'step');
});

it('should set aria-live="polite" on the current step', () => {
cy.get('post-stepper')
.invoke('attr', 'current-index', 1)
.wait(100)
.find('post-stepper-item')
.eq(1)
.should('have.attr', 'aria-live', 'polite');
});

it('should not have aria-live="polite" on future step', () => {
cy.get('post-stepper')
.invoke('attr', 'current-index', 1)
.wait(100)
.find('post-stepper-item')
.eq(2)
.should('not.have.attr', 'aria-live');
});

it('should set current label on the current step', () => {
cy.get('post-stepper')
.invoke('attr', 'current-index', 1)
.wait(100)
.find('post-stepper-item')
.eq(1)
.find('.step-hidden-label')
.should('have.text', 'Current step:');
});

it('should set completed label on completed step', () => {
cy.get('post-stepper')
.invoke('attr', 'current-index', 1)
.wait(100)
.find('post-stepper-item')
.eq(0)
.find('.step-hidden-label')
.should('have.text', 'Completed step:');
});

it('should set correct mobile label on current step', () => {
cy.get('post-stepper')
.invoke('attr', 'current-index', 1)
.wait(100)
.find('post-stepper-item')
.eq(1)
.find('.step-mobile-label')
.should('have.text', 'Step 2:');
});

// Dynamically added/removed steps

it('should add correct class when a new step is added dynamically', () => {
cy.get('post-stepper').find('post-stepper-item').should('have.length', 5);
cy.get('post-stepper').then($stepper => {
$stepper[0].appendChild(document.createElement('post-stepper-item'));
cy.get('post-stepper')
.wait(100)
.find('post-stepper-item')
.should('have.length', 6)
.last()
.should('have.class', 'stepper-item-inactive');
});
});

it('should throw an error if there is only one step', () => {
cy.window().then(win => {
cy.spy(win.console, 'error').as('consoleError');
});
cy.get('post-stepper').find('post-stepper-item').should('have.length', 5);

cy.get('post-stepper').then($stepper => {
const stepper = $stepper[0];
const allButFirstStep = stepper.querySelectorAll('post-stepper-item:not(:first-child)');
allButFirstStep.forEach(step => step.remove());
});

cy.get('post-stepper').find('post-stepper-item').should('have.length', 1);
cy.get('@consoleError').should('be.called');
});
});
22 changes: 22 additions & 0 deletions packages/components/cypress/fixtures/post-stepper.test.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link
href="../../node_modules/@swisspost/design-system-styles/post-default.css"
rel="stylesheet"
/>
<script src="../../dist/post-components/post-components.esm.js" type="module"></script>
</head>
<body>
<post-stepper completed-label="Completed step" current-label="Current step" step-label="Step">
<post-stepper-item> Step 1</post-stepper-item>
<post-stepper-item> Step 2</post-stepper-item>
<post-stepper-item> Step 3</post-stepper-item>
<post-stepper-item> Step 4</post-stepper-item>
<post-stepper-item> Step 5</post-stepper-item>
</post-stepper>
</body>
</html>
60 changes: 60 additions & 0 deletions packages/components/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,27 @@ export namespace Components {
*/
"stars": number;
}
interface PostStepper {
/**
* "Completed step" label for accessibility
*/
"completedLabel": string;
/**
* Defines the currently active step
* @default -1
*/
"currentIndex": number;
/**
* "Current step" label for accessibility
*/
"currentLabel": string;
/**
* "Step" label for mobile view
*/
"stepLabel": string;
}
interface PostStepperItem {
}
interface PostTabHeader {
/**
* The name of the panel controlled by the tab header.
Expand Down Expand Up @@ -851,6 +872,18 @@ declare global {
prototype: HTMLPostRatingElement;
new (): HTMLPostRatingElement;
};
interface HTMLPostStepperElement extends Components.PostStepper, HTMLStencilElement {
}
var HTMLPostStepperElement: {
prototype: HTMLPostStepperElement;
new (): HTMLPostStepperElement;
};
interface HTMLPostStepperItemElement extends Components.PostStepperItem, HTMLStencilElement {
}
var HTMLPostStepperItemElement: {
prototype: HTMLPostStepperItemElement;
new (): HTMLPostStepperItemElement;
};
interface HTMLPostTabHeaderElement extends Components.PostTabHeader, HTMLStencilElement {
}
var HTMLPostTabHeaderElement: {
Expand Down Expand Up @@ -928,6 +961,8 @@ declare global {
"post-popover": HTMLPostPopoverElement;
"post-popovercontainer": HTMLPostPopovercontainerElement;
"post-rating": HTMLPostRatingElement;
"post-stepper": HTMLPostStepperElement;
"post-stepper-item": HTMLPostStepperItemElement;
"post-tab-header": HTMLPostTabHeaderElement;
"post-tab-panel": HTMLPostTabPanelElement;
"post-tabs": HTMLPostTabsElement;
Expand Down Expand Up @@ -1323,6 +1358,27 @@ declare namespace LocalJSX {
*/
"stars"?: number;
}
interface PostStepper {
/**
* "Completed step" label for accessibility
*/
"completedLabel": string;
/**
* Defines the currently active step
* @default -1
*/
"currentIndex"?: number;
/**
* "Current step" label for accessibility
*/
"currentLabel": string;
/**
* "Step" label for mobile view
*/
"stepLabel": string;
}
interface PostStepperItem {
}
interface PostTabHeader {
/**
* The name of the panel controlled by the tab header.
Expand Down Expand Up @@ -1419,6 +1475,8 @@ declare namespace LocalJSX {
"post-popover": PostPopover;
"post-popovercontainer": PostPopovercontainer;
"post-rating": PostRating;
"post-stepper": PostStepper;
"post-stepper-item": PostStepperItem;
"post-tab-header": PostTabHeader;
"post-tab-panel": PostTabPanel;
"post-tabs": PostTabs;
Expand Down Expand Up @@ -1466,6 +1524,8 @@ declare module "@stencil/core" {
"post-popover": LocalJSX.PostPopover & JSXBase.HTMLAttributes<HTMLPostPopoverElement>;
"post-popovercontainer": LocalJSX.PostPopovercontainer & JSXBase.HTMLAttributes<HTMLPostPopovercontainerElement>;
"post-rating": LocalJSX.PostRating & JSXBase.HTMLAttributes<HTMLPostRatingElement>;
"post-stepper": LocalJSX.PostStepper & JSXBase.HTMLAttributes<HTMLPostStepperElement>;
"post-stepper-item": LocalJSX.PostStepperItem & JSXBase.HTMLAttributes<HTMLPostStepperItemElement>;
"post-tab-header": LocalJSX.PostTabHeader & JSXBase.HTMLAttributes<HTMLPostTabHeaderElement>;
"post-tab-panel": LocalJSX.PostTabPanel & JSXBase.HTMLAttributes<HTMLPostTabPanelElement>;
"post-tabs": LocalJSX.PostTabs & JSXBase.HTMLAttributes<HTMLPostTabsElement>;
Expand Down
Loading
Loading