Skip to content

Commit

Permalink
feat: updates to stepper component (breaking change + new tag attribu…
Browse files Browse the repository at this point in the history
…te) (#573)

* First iteration of stepper V2

* Add new tag property to stepper

* Add validation to gcds-stepper properties

* Update design tokens package version

* Add validation + expand tests

* Add one more unit test

* Add French version of error

* Add required to default slot in storybook

* Update test names + linting

* Add new logError function for property validation

* Update tokens package
  • Loading branch information
ethanWallace committed Aug 19, 2024
1 parent 4a72105 commit 0f8bd2f
Show file tree
Hide file tree
Showing 12 changed files with 407 additions and 47 deletions.
4 changes: 2 additions & 2 deletions packages/angular/src/lib/stencil-generated/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1096,14 +1096,14 @@ export declare interface GcdsSrOnly extends Components.GcdsSrOnly {}


@ProxyCmp({
inputs: ['currentStep', 'totalSteps']
inputs: ['currentStep', 'tag', 'totalSteps']
})
@Component({
selector: 'gcds-stepper',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['currentStep', 'totalSteps'],
inputs: ['currentStep', 'tag', 'totalSteps'],
})
export class GcdsStepper {
protected el: HTMLElement;
Expand Down
3 changes: 2 additions & 1 deletion packages/vue/lib/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,8 @@ export const GcdsSrOnly = /*@__PURE__*/ defineContainer<JSX.GcdsSrOnly>('gcds-sr

export const GcdsStepper = /*@__PURE__*/ defineContainer<JSX.GcdsStepper>('gcds-stepper', undefined, [
'currentStep',
'totalSteps'
'totalSteps',
'tag'
]);


Expand Down
8 changes: 8 additions & 0 deletions packages/web/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1017,6 +1017,10 @@ export namespace Components {
* Defines the current step.
*/
"currentStep": number;
/**
* Defines the heading tag to render
*/
"tag": 'h1' | 'h2' | 'h3';
/**
* Defines the total amount of steps.
*/
Expand Down Expand Up @@ -3014,6 +3018,10 @@ declare namespace LocalJSX {
* Defines the current step.
*/
"currentStep": number;
/**
* Defines the heading tag to render
*/
"tag"?: 'h1' | 'h2' | 'h3';
/**
* Defines the total amount of steps.
*/
Expand Down
2 changes: 2 additions & 0 deletions packages/web/src/components/gcds-sr-only/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
- [gcds-footer](../gcds-footer)
- [gcds-lang-toggle](../gcds-lang-toggle)
- [gcds-search](../gcds-search)
- [gcds-stepper](../gcds-stepper)
- [gcds-topic-menu](../gcds-topic-menu)

### Graph
Expand All @@ -31,6 +32,7 @@ graph TD;
gcds-footer --> gcds-sr-only
gcds-lang-toggle --> gcds-sr-only
gcds-search --> gcds-sr-only
gcds-stepper --> gcds-sr-only
gcds-topic-menu --> gcds-sr-only
style gcds-sr-only fill:#f9f,stroke:#333,stroke-width:4px
```
Expand Down
11 changes: 9 additions & 2 deletions packages/web/src/components/gcds-stepper/gcds-stepper.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,14 @@
}

@layer default {
:host .gcds-stepper {
color: var(--gcds-stepper-text);
:host .gcds-stepper .gcds-stepper__steps {
font: var(--gcds-stepper-font-desktop);
display: block;
margin: var(--gcds-stepper-margin-desktop);

@media only screen and (width < 48em) {
font: var(--gcds-stepper-font-mobile);
margin: var(--gcds-stepper-margin-mobile);
}
}
}
98 changes: 85 additions & 13 deletions packages/web/src/components/gcds-stepper/gcds-stepper.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Component, Element, Host, Prop, State, h } from '@stencil/core';
import { assignLanguage, observerConfig } from '../../utils/utils';
import { Component, Element, Host, Prop, State, Watch, h } from '@stencil/core';
import { assignLanguage, observerConfig, logError } from '../../utils/utils';
import i18n from './i18n/i18n';

@Component({
Expand All @@ -17,12 +17,47 @@ export class GcdsStepper {
/**
* Defines the current step.
*/
@Prop() currentStep!: number;
@Prop({ mutable: true }) currentStep!: number;
@Watch('currentStep')
validateCurrentStep() {
if (
this.currentStep <= 0 ||
isNaN(this.currentStep) ||
this.currentStep > this.totalSteps
) {
this.errors.push('currentStep');
} else if (this.errors.includes('currentStep')) {
this.errors.splice(this.errors.indexOf('currentStep'), 1);
}
}

/**
* Defines the total amount of steps.
*/
@Prop() totalSteps!: number;
@Prop({ mutable: true }) totalSteps!: number;
@Watch('totalSteps')
validateTotalSteps() {
if (
this.totalSteps <= 0 ||
isNaN(this.totalSteps) ||
this.totalSteps < this.currentStep
) {
this.errors.push('totalSteps');
} else if (this.errors.includes('totalSteps')) {
this.errors.splice(this.errors.indexOf('totalSteps'), 1);
}
}

/**
* Defines the heading tag to render
*/
@Prop() tag: 'h1' | 'h2' | 'h3' = 'h2';

/**
* State to track validation on properties
* Contains a list of properties that have an error associated with them
*/
@State() errors: Array<string> = [];

/**
* Language of rendered component
Expand All @@ -41,26 +76,63 @@ export class GcdsStepper {
observer.observe(this.el, observerConfig);
}

private validateChildren() {
if (this.el.innerHTML.trim() == '') {
this.errors.push('children');
} else if (this.errors.includes('children')) {
this.errors.splice(this.errors.indexOf('children'), 1);
}
}

private validateRequiredProps() {
this.validateCurrentStep();
this.validateTotalSteps();
this.validateChildren();

if (
this.errors.includes('totalSteps') ||
this.errors.includes('currentStep') ||
this.errors.includes('children')
) {
return false;
}
return true;
}

async componentWillLoad() {
// Define lang attribute
this.lang = assignLanguage(this.el);

this.updateLang();

let valid = this.validateRequiredProps();

if (!valid) {
logError('gcds-stepper', this.errors);
}
}

render() {
const { currentStep, lang, totalSteps } = this;
const { currentStep, lang, totalSteps, tag } = this;

return (
<Host>
<gcds-heading
tag="h6"
class="gcds-stepper"
margin-top="0"
margin-bottom="300"
>
{`${i18n[lang].step} ${currentStep} ${i18n[lang].of} ${totalSteps}`}
</gcds-heading>
{this.validateRequiredProps() && (
<gcds-heading
tag={tag}
class="gcds-stepper"
margin-top="0"
margin-bottom="300"
>
<span class="gcds-stepper__steps">
{`${i18n[lang].step} ${currentStep} ${i18n[lang].of} ${totalSteps}`}

{/* Hidden colon to provide pause between caption and heading text for AT */}
<gcds-sr-only> : </gcds-sr-only>
</span>
<slot></slot>
</gcds-heading>
)}
</Host>
);
}
Expand Down
11 changes: 7 additions & 4 deletions packages/web/src/components/gcds-stepper/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,25 @@

## Properties

| Property | Attribute | Description | Type | Default |
| -------------------------- | -------------- | ---------------------------------- | -------- | ----------- |
| `currentStep` _(required)_ | `current-step` | Defines the current step. | `number` | `undefined` |
| `totalSteps` _(required)_ | `total-steps` | Defines the total amount of steps. | `number` | `undefined` |
| Property | Attribute | Description | Type | Default |
| -------------------------- | -------------- | ---------------------------------- | ---------------------- | ----------- |
| `currentStep` _(required)_ | `current-step` | Defines the current step. | `number` | `undefined` |
| `tag` | `tag` | Defines the heading tag to render | `"h1" \| "h2" \| "h3"` | `'h2'` |
| `totalSteps` _(required)_ | `total-steps` | Defines the total amount of steps. | `number` | `undefined` |


## Dependencies

### Depends on

- [gcds-heading](../gcds-heading)
- [gcds-sr-only](../gcds-sr-only)

### Graph
```mermaid
graph TD;
gcds-stepper --> gcds-heading
gcds-stepper --> gcds-sr-only
style gcds-stepper fill:#f9f,stroke:#333,stroke-width:4px
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,29 @@ export default {
required: true,
},
},
tag: {
control: 'select',
options: ['h1', 'h2', 'h3'],
table: {
type: { summary: 'string' },
defaultValue: { summary: 'h2' },
},
},

// Slots
default: {
control: {
type: 'text',
},
description:
'Customize the content or include additional elements. | Personnalisez le contenu ou ajoutez des éléments supplémentaires.',
table: {
category: 'Slots | Fentes',
},
type: {
required: true,
},
},
...langProp,
},
};
Expand All @@ -36,22 +59,25 @@ const Template = args =>
<!-- Web component code (HTML, Angular, Vue) -->
<gcds-stepper current-step="${args.currentStep}" total-steps="${
args.totalSteps
}" ${args.lang != 'en' ? `lang="${args.lang}"` : null}>
}" tag="${args.tag}" ${args.lang != 'en' ? `lang="${args.lang}"` : null}>
${args.default}
</gcds-stepper>
<!-- React code -->
<GcdsStepper currentStep="${args.currentStep}" totalSteps="${
args.totalSteps
}" ${args.lang != 'en' ? `lang="${args.lang}"` : null}>
}" tag="${args.tag}" ${args.lang != 'en' ? `lang="${args.lang}"` : null}>
${args.default}
</GcdsStepper>
`.replace(/ null/g, '');

const TemplatePlayground = args => `
<gcds-stepper
current-step="${args.currentStep}"
total-steps="${args.totalSteps}"
${args.lang != 'en' ? `lang="${args.lang}"` : null}
>
tag="${args.tag}"
${args.lang != 'en' ? `lang="${args.lang}"` : null}>
${args.default}
</gcds-stepper>
`;

Expand All @@ -62,6 +88,41 @@ Default.args = {
lang: 'en',
currentStep: 1,
totalSteps: 4,
tag: 'h2',
default: 'Section title',
};

// ------ Stepper tag: H1 ------

export const tagH1 = Template.bind({});
tagH1.args = {
lang: 'en',
currentStep: 1,
totalSteps: 4,
tag: 'h1',
default: 'Section title',
};

// ------ Stepper tag: H2 ------

export const tagH2 = Template.bind({});
tagH2.args = {
lang: 'en',
currentStep: 1,
totalSteps: 4,
tag: 'h2',
default: 'Section title',
};

// ------ Stepper tag: H3 ------

export const tagH3 = Template.bind({});
tagH3.args = {
lang: 'en',
currentStep: 1,
totalSteps: 4,
tag: 'h3',
default: 'Section title',
};

// ------ Stepper french ------
Expand All @@ -71,6 +132,8 @@ French.args = {
lang: 'fr',
currentStep: 1,
totalSteps: 4,
tag: 'h2',
default: 'Section title',
};

// ------ Stepper events & props ------
Expand All @@ -80,6 +143,8 @@ Props.args = {
lang: 'en',
currentStep: 1,
totalSteps: 4,
tag: 'h2',
default: 'Section title',
};

// ------ Stepper playground ------
Expand All @@ -89,4 +154,6 @@ Playground.args = {
lang: 'en',
currentStep: 1,
totalSteps: 4,
tag: 'h2',
default: 'Section title',
};
14 changes: 14 additions & 0 deletions packages/web/src/components/gcds-stepper/stories/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,20 @@ A stepper is a progress tracker for a multi-step process.

<br />

### Tag

#### Heading 1

<Canvas of={Stepper.tagH1} story={{ inline: true }} />

#### Heading 2

<Canvas of={Stepper.tagH2} story={{ inline: true }} />

#### Heading 3

<Canvas of={Stepper.tagH3} story={{ inline: true }} />

### Language

#### English
Expand Down
Loading

0 comments on commit 0f8bd2f

Please sign in to comment.