Skip to content

Commit 56d4461

Browse files
committed
HDS-4411 Stepper::Navigation and Stepper::List components added
1 parent 431d5ae commit 56d4461

File tree

19 files changed

+1388
-8
lines changed

19 files changed

+1388
-8
lines changed

packages/components/package.json

+4
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,10 @@
297297
"./components/hds/side-nav/portal/index.js": "./dist/_app_/components/hds/side-nav/portal/index.js",
298298
"./components/hds/side-nav/portal/target.js": "./dist/_app_/components/hds/side-nav/portal/target.js",
299299
"./components/hds/side-nav/toggle-button.js": "./dist/_app_/components/hds/side-nav/toggle-button.js",
300+
"./components/hds/stepper/list/index.js": "./dist/_app_/components/hds/stepper/list/index.js",
301+
"./components/hds/stepper/list/step.js": "./dist/_app_/components/hds/stepper/list/step.js",
302+
"./components/hds/stepper/navigation/index.js": "./dist/_app_/components/hds/stepper/navigation/index.js",
303+
"./components/hds/stepper/navigation/step.js": "./dist/_app_/components/hds/stepper/navigation/step.js",
300304
"./components/hds/stepper/step/indicator.js": "./dist/_app_/components/hds/stepper/step/indicator.js",
301305
"./components/hds/stepper/task/indicator.js": "./dist/_app_/components/hds/stepper/task/indicator.js",
302306
"./components/hds/table/index.js": "./dist/_app_/components/hds/table/index.js",

packages/components/src/components.ts

+4
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,10 @@ export { default as HdsSideNavPortalTarget } from './components/hds/side-nav/por
257257
export { default as HdsSideNavToggleButton } from './components/hds/side-nav/toggle-button.ts';
258258

259259
// Stepper
260+
export { default as HdsStepperList } from './components/hds/stepper/list/index.ts';
261+
export { default as HdsStepperListStep } from './components/hds/stepper/list/step.ts';
262+
export { default as HdsStepperNavigation } from './components/hds/stepper/navigation/index.ts';
263+
export { default as HdsStepperNavigationStep } from './components/hds/stepper/navigation/step.ts';
260264
export { default as HdsStepperStepIndicator } from './components/hds/stepper/step/indicator.ts';
261265
export { default as HdsStepperTaskIndicator } from './components/hds/stepper/task/indicator.ts';
262266
export * from './components/hds/stepper/types.ts';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{{!
2+
Copyright (c) HashiCorp, Inc.
3+
SPDX-License-Identifier: MPL-2.0
4+
}}
5+
<ul class={{this.classNames}} ...attributes>
6+
{{yield
7+
(hash
8+
Step=(component
9+
"hds/stepper/list/step"
10+
currentStep=this.currentStep
11+
titleTag=this.titleTag
12+
didInsertNode=this.didInsertStep
13+
stepIds=this._stepIds
14+
)
15+
)
16+
to="steps"
17+
}}
18+
</ul>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/**
2+
* Copyright (c) HashiCorp, Inc.
3+
* SPDX-License-Identifier: MPL-2.0
4+
*/
5+
6+
import Component from '@glimmer/component';
7+
import { tracked } from '@glimmer/tracking';
8+
import { action } from '@ember/object';
9+
import type { ComponentLike } from '@glint/template';
10+
import type { HdsStepperListStepSignature } from './step';
11+
12+
import { HdsStepperTitleTagValues } from '../types.ts';
13+
import type { HdsStepperTitleTags, HdsStepperListStepIds } from '../types.ts';
14+
15+
export interface HdsStepperListSignature {
16+
Args: {
17+
currentStep?: number;
18+
titleTag?: HdsStepperTitleTags;
19+
};
20+
Blocks: {
21+
steps?: [
22+
{
23+
Step?: ComponentLike<HdsStepperListStepSignature>;
24+
},
25+
];
26+
};
27+
Element: HTMLElement;
28+
}
29+
30+
export default class HdsStepperList extends Component<HdsStepperListSignature> {
31+
@tracked private _stepIds: HdsStepperListStepIds = [];
32+
@tracked private _stepNodes: HTMLElement[] = [];
33+
34+
/**
35+
* Get the DOM tag that should be used for the title
36+
* @param titleTag
37+
* @type {HdsStepperTitleTags}
38+
* @default 'div'
39+
*/
40+
get titleTag(): HdsStepperTitleTags {
41+
return this.args.titleTag ?? HdsStepperTitleTagValues.Div;
42+
}
43+
44+
/**
45+
* @param currentStep
46+
* @type {number}
47+
* @default 0
48+
*/
49+
get currentStep(): number {
50+
const { currentStep } = this.args;
51+
52+
if (currentStep) {
53+
if (currentStep < 0) {
54+
return 0;
55+
} else {
56+
return currentStep;
57+
}
58+
} else {
59+
return 0;
60+
}
61+
}
62+
63+
@action
64+
didInsertStep(element: HTMLElement): void {
65+
this._stepIds = [...this._stepIds, element.id];
66+
this._stepNodes = [...this._stepNodes, element];
67+
}
68+
69+
/**
70+
* Get the class names to apply to the component.
71+
* @method classNames
72+
* @return {string} The "class" attribute to apply to the component.
73+
*/
74+
get classNames() {
75+
const classes = ['hds-stepper-list'];
76+
77+
return classes.join(' ');
78+
}
79+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{{!
2+
Copyright (c) HashiCorp, Inc.
3+
SPDX-License-Identifier: MPL-2.0
4+
}}
5+
<li {{did-insert this.didInsertNode}} class={{this.classNames}} ...attributes id={{this._stepId}}>
6+
<div class="hds-stepper-list__step__progress">
7+
<Hds::Stepper::Step::Indicator
8+
@text="{{this.stepNumber}}"
9+
@status={{this.status}}
10+
@isInteractive={{false}}
11+
class="hds-stepper-list__step__indicator"
12+
/>
13+
</div>
14+
<div class="hds-stepper-list__step__text">
15+
<span class="sr-only">{{this.statusSrOnlyText}}</span>
16+
<Hds::Text::Display
17+
class="hds-stepper-list__step__title"
18+
@tag={{this.titleTag}}
19+
@size="300"
20+
@weight="medium"
21+
@color="primary"
22+
>
23+
{{yield to="title"}}
24+
</Hds::Text::Display>
25+
<Hds::Text::Body
26+
class="hds-stepper-list__step__description"
27+
@tag="div"
28+
@size="200"
29+
@weight="regular"
30+
@color="faint"
31+
>
32+
{{yield to="description"}}
33+
</Hds::Text::Body>
34+
<div class="hds-stepper-list__step__content">
35+
{{yield to="content"}}
36+
</div>
37+
</div>
38+
</li>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/**
2+
* Copyright (c) HashiCorp, Inc.
3+
* SPDX-License-Identifier: MPL-2.0
4+
*/
5+
6+
import Component from '@glimmer/component';
7+
import { assert } from '@ember/debug';
8+
import { guidFor } from '@ember/object/internals';
9+
import { action } from '@ember/object';
10+
11+
import {
12+
HdsStepperStatusesValues,
13+
HdsStepperTitleTagValues,
14+
} from '../types.ts';
15+
import {
16+
type HdsStepperStatuses,
17+
type HdsStepperTitleTags,
18+
type HdsStepperListStepIds,
19+
HdsStepperStatusToSrOnlyText,
20+
} from '../types.ts';
21+
22+
export const DEFAULT_STATUS = HdsStepperStatusesValues.Incomplete;
23+
export const STATUSES: string[] = Object.values(HdsStepperStatusesValues);
24+
export const MAPPING_STATUS_TO_SR_ONLY_TEXT = HdsStepperStatusToSrOnlyText;
25+
26+
export interface HdsStepperListStepSignature {
27+
Args: {
28+
status: HdsStepperStatusesValues;
29+
currentStep: number;
30+
stepNumber?: number;
31+
titleTag?: HdsStepperTitleTags;
32+
stepIds: HdsStepperListStepIds;
33+
didInsertNode?: (element: HTMLElement) => void;
34+
};
35+
Blocks: {
36+
title: [];
37+
description?: [];
38+
content?: [];
39+
};
40+
Element: HTMLElement;
41+
}
42+
43+
export default class HdsStepperListStep extends Component<HdsStepperListStepSignature> {
44+
/**
45+
* Generate a unique ID for the Step
46+
* @return {string}
47+
* @param _stepId
48+
*/
49+
private _stepId = 'step-' + guidFor(this);
50+
51+
/**
52+
* Get the index of the step from the _stepIds list
53+
* @param nodeIndex
54+
* @type {number}
55+
*/
56+
get nodeIndex(): number | undefined {
57+
return this.args.stepIds?.indexOf(this._stepId);
58+
}
59+
60+
/**
61+
* Get the step number that should be displayed
62+
* @param stepNumber
63+
* @type {number}
64+
*/
65+
get stepNumber(): number | undefined {
66+
return this.args.stepNumber ?? this.args.stepIds?.indexOf(this._stepId) + 1;
67+
}
68+
69+
/**
70+
* @param status
71+
* @type {HdsStepperStatuses}
72+
* @default "incomplete"
73+
*/
74+
get status(): HdsStepperStatuses {
75+
const { status = DEFAULT_STATUS } = this.args;
76+
77+
assert(
78+
`@status for "Hds::Stepper::Step::Indicator" must be one of the following: ${STATUSES.join(
79+
', '
80+
)}; received: ${status}`,
81+
STATUSES.includes(status)
82+
);
83+
84+
return status;
85+
}
86+
87+
/**
88+
* Get the screen reader only text that should be added based on the step status
89+
* @param statusSrOnlyText
90+
* @type {string}
91+
* @default ''
92+
*/
93+
get statusSrOnlyText(): string {
94+
return MAPPING_STATUS_TO_SR_ONLY_TEXT[this.status];
95+
}
96+
97+
/**
98+
* Get the DOM tag that should be used for the title
99+
* @param titleTag
100+
* @type {HdsStepperTitleTags}
101+
* @default 'div'
102+
*/
103+
get titleTag(): HdsStepperTitleTags {
104+
return this.args.titleTag ?? HdsStepperTitleTagValues.Div;
105+
}
106+
107+
@action
108+
didInsertNode(element: HTMLElement): void {
109+
const { didInsertNode } = this.args;
110+
111+
if (typeof didInsertNode === 'function' && this._stepId != undefined) {
112+
didInsertNode(element);
113+
}
114+
}
115+
116+
/**
117+
* Get the class names to apply to the component.
118+
* @method classNames
119+
* @return {string} The "class" attribute to apply to the component.
120+
*/
121+
get classNames(): string {
122+
const classes = ['hds-stepper-list__step'];
123+
124+
return classes.join(' ');
125+
}
126+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{{!
2+
Copyright (c) HashiCorp, Inc.
3+
SPDX-License-Identifier: MPL-2.0
4+
}}
5+
<div class={{this.classNames}} ...attributes>
6+
<ul class="hds-stepper-navigation__step-list">
7+
{{#if @steps}}
8+
{{#each @steps as |step|}}
9+
<Hds::Stepper::Navigation::Step
10+
@currentStep={{this.currentStep}}
11+
@isComplete={{step.isComplete}}
12+
@isInteractive={{step.isInteractive}}
13+
@titleTag={{this.titleTag}}
14+
@didInsertNode={{this.didInsertStep}}
15+
@stepIds={{this._stepIds}}
16+
@onStepChange={{@onStepChange}}
17+
@onKeyUp={{this.onKeyUp}}
18+
>
19+
<:title>{{step.title}}</:title>
20+
<:description>{{step.description}}</:description>
21+
</Hds::Stepper::Navigation::Step>
22+
{{/each}}
23+
{{else}}
24+
{{yield
25+
(hash
26+
Step=(component
27+
"hds/stepper/navigation/step"
28+
currentStep=this.currentStep
29+
titleTag=this.titleTag
30+
didInsertNode=this.didInsertStep
31+
stepIds=this._stepIds
32+
onStepChange=@onStepChange
33+
onKeyUp=this.onKeyUp
34+
)
35+
)
36+
to="steps"
37+
}}
38+
{{/if}}
39+
</ul>
40+
</div>
41+
{{yield to="body"}}

0 commit comments

Comments
 (0)