@pequity/squirrel 8.0.2 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/chunks/p-steps.js +52 -0
- package/dist/cjs/index.js +2 -0
- package/dist/cjs/p-steps.js +3 -0
- package/dist/es/chunks/p-steps.js +53 -0
- package/dist/es/index.js +8 -6
- package/dist/es/p-steps.js +4 -0
- package/dist/squirrel/components/index.d.ts +2 -1
- package/dist/squirrel/components/p-steps/p-steps.vue.d.ts +17 -0
- package/package.json +1 -1
- package/squirrel/components/index.ts +2 -0
- package/squirrel/components/p-steps/__snapshots__/p-steps.spec.js.snap +16 -0
- package/squirrel/components/p-steps/p-steps.spec.js +126 -0
- package/squirrel/components/p-steps/p-steps.stories.js +31 -0
- package/squirrel/components/p-steps/p-steps.vue +47 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const vue = require("vue");
|
|
3
|
+
const pIcon_vue_vue_type_script_setup_true_lang = require("./p-icon.js");
|
|
4
|
+
const lodashEs = require("lodash-es");
|
|
5
|
+
const _hoisted_1 = { class: "flex items-center gap-2" };
|
|
6
|
+
const _hoisted_2 = {
|
|
7
|
+
key: 0,
|
|
8
|
+
class: "flex items-center"
|
|
9
|
+
};
|
|
10
|
+
const _sfc_main = /* @__PURE__ */ vue.defineComponent({
|
|
11
|
+
__name: "p-steps",
|
|
12
|
+
props: {
|
|
13
|
+
steps: {},
|
|
14
|
+
currentStep: {},
|
|
15
|
+
stepTitleMap: {}
|
|
16
|
+
},
|
|
17
|
+
setup(__props) {
|
|
18
|
+
const props = __props;
|
|
19
|
+
const currentStepIndex = vue.computed(() => props.steps.findIndex((s) => s === props.currentStep));
|
|
20
|
+
const stepClasses = (step, stepIndex) => {
|
|
21
|
+
if (step === props.currentStep) {
|
|
22
|
+
return "border border-p-blue-50 bg-p-blue-50 text-surface";
|
|
23
|
+
}
|
|
24
|
+
if (currentStepIndex.value < stepIndex) {
|
|
25
|
+
return "border border-p-gray-30 text-p-gray-30";
|
|
26
|
+
}
|
|
27
|
+
return "border border-p-blue-50 text-p-blue-50";
|
|
28
|
+
};
|
|
29
|
+
const stepTitle = (step) => {
|
|
30
|
+
var _a;
|
|
31
|
+
return ((_a = props.stepTitleMap) == null ? void 0 : _a[step]) || lodashEs.startCase(step);
|
|
32
|
+
};
|
|
33
|
+
return (_ctx, _cache) => {
|
|
34
|
+
return vue.openBlock(), vue.createElementBlock("div", _hoisted_1, [
|
|
35
|
+
(vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(_ctx.steps, (step, idx) => {
|
|
36
|
+
return vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: step }, [
|
|
37
|
+
vue.createElementVNode("div", {
|
|
38
|
+
class: vue.normalizeClass(["text-nowrap rounded-full border px-4 py-1 text-sm font-semibold", stepClasses(step, idx)])
|
|
39
|
+
}, vue.toDisplayString(stepTitle(step)), 3),
|
|
40
|
+
idx < _ctx.steps.length - 1 ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_2, [
|
|
41
|
+
vue.createVNode(pIcon_vue_vue_type_script_setup_true_lang._sfc_main, {
|
|
42
|
+
icon: "material-symbols:arrow-right-alt-rounded",
|
|
43
|
+
class: vue.normalizeClass([currentStepIndex.value <= idx ? "text-p-gray-30" : "text-p-blue-50"])
|
|
44
|
+
}, null, 8, ["class"])
|
|
45
|
+
])) : vue.createCommentVNode("", true)
|
|
46
|
+
], 64);
|
|
47
|
+
}), 128))
|
|
48
|
+
]);
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
exports._sfc_main = _sfc_main;
|
package/dist/cjs/index.js
CHANGED
|
@@ -42,6 +42,7 @@ const string = require("./string.js");
|
|
|
42
42
|
const text = require("./text.js");
|
|
43
43
|
const pSelectPill = require("./p-select-pill.js");
|
|
44
44
|
const pSkeletonLoader = require("./p-skeleton-loader.js");
|
|
45
|
+
const pSteps_vue_vue_type_script_setup_true_lang = require("./chunks/p-steps.js");
|
|
45
46
|
const pTable$1 = require("./p-table.js");
|
|
46
47
|
const usePTableColResize = require("./usePTableColResize.js");
|
|
47
48
|
const pTableHeaderCell = require("./p-table-header-cell.js");
|
|
@@ -1019,6 +1020,7 @@ exports.toString = string.toString;
|
|
|
1019
1020
|
exports.splitStringForHighlight = text.splitStringForHighlight;
|
|
1020
1021
|
exports.PSelectPill = pSelectPill;
|
|
1021
1022
|
exports.PSkeletonLoader = pSkeletonLoader;
|
|
1023
|
+
exports.PSteps = pSteps_vue_vue_type_script_setup_true_lang._sfc_main;
|
|
1022
1024
|
exports.MIN_WIDTH_COL_RESIZE = pTable$1.MIN_WIDTH_COL_RESIZE;
|
|
1023
1025
|
exports.colsInjectionKey = pTable$1.colsInjectionKey;
|
|
1024
1026
|
exports.isColsResizableInjectionKey = pTable$1.isColsResizableInjectionKey;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { defineComponent, computed, createElementBlock, openBlock, Fragment, renderList, createElementVNode, createCommentVNode, normalizeClass, toDisplayString, createVNode } from "vue";
|
|
2
|
+
import { _ as _sfc_main$1 } from "./p-icon.js";
|
|
3
|
+
import { startCase } from "lodash-es";
|
|
4
|
+
const _hoisted_1 = { class: "flex items-center gap-2" };
|
|
5
|
+
const _hoisted_2 = {
|
|
6
|
+
key: 0,
|
|
7
|
+
class: "flex items-center"
|
|
8
|
+
};
|
|
9
|
+
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
10
|
+
__name: "p-steps",
|
|
11
|
+
props: {
|
|
12
|
+
steps: {},
|
|
13
|
+
currentStep: {},
|
|
14
|
+
stepTitleMap: {}
|
|
15
|
+
},
|
|
16
|
+
setup(__props) {
|
|
17
|
+
const props = __props;
|
|
18
|
+
const currentStepIndex = computed(() => props.steps.findIndex((s) => s === props.currentStep));
|
|
19
|
+
const stepClasses = (step, stepIndex) => {
|
|
20
|
+
if (step === props.currentStep) {
|
|
21
|
+
return "border border-p-blue-50 bg-p-blue-50 text-surface";
|
|
22
|
+
}
|
|
23
|
+
if (currentStepIndex.value < stepIndex) {
|
|
24
|
+
return "border border-p-gray-30 text-p-gray-30";
|
|
25
|
+
}
|
|
26
|
+
return "border border-p-blue-50 text-p-blue-50";
|
|
27
|
+
};
|
|
28
|
+
const stepTitle = (step) => {
|
|
29
|
+
var _a;
|
|
30
|
+
return ((_a = props.stepTitleMap) == null ? void 0 : _a[step]) || startCase(step);
|
|
31
|
+
};
|
|
32
|
+
return (_ctx, _cache) => {
|
|
33
|
+
return openBlock(), createElementBlock("div", _hoisted_1, [
|
|
34
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.steps, (step, idx) => {
|
|
35
|
+
return openBlock(), createElementBlock(Fragment, { key: step }, [
|
|
36
|
+
createElementVNode("div", {
|
|
37
|
+
class: normalizeClass(["text-nowrap rounded-full border px-4 py-1 text-sm font-semibold", stepClasses(step, idx)])
|
|
38
|
+
}, toDisplayString(stepTitle(step)), 3),
|
|
39
|
+
idx < _ctx.steps.length - 1 ? (openBlock(), createElementBlock("div", _hoisted_2, [
|
|
40
|
+
createVNode(_sfc_main$1, {
|
|
41
|
+
icon: "material-symbols:arrow-right-alt-rounded",
|
|
42
|
+
class: normalizeClass([currentStepIndex.value <= idx ? "text-p-gray-30" : "text-p-blue-50"])
|
|
43
|
+
}, null, 8, ["class"])
|
|
44
|
+
])) : createCommentVNode("", true)
|
|
45
|
+
], 64);
|
|
46
|
+
}), 128))
|
|
47
|
+
]);
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
export {
|
|
52
|
+
_sfc_main as _
|
|
53
|
+
};
|
package/dist/es/index.js
CHANGED
|
@@ -41,6 +41,7 @@ import { toString } from "./string.js";
|
|
|
41
41
|
import { splitStringForHighlight } from "./text.js";
|
|
42
42
|
import { default as default9 } from "./p-select-pill.js";
|
|
43
43
|
import { default as default10 } from "./p-skeleton-loader.js";
|
|
44
|
+
import { _ as _19 } from "./chunks/p-steps.js";
|
|
44
45
|
import { colsInjectionKey, isFirstColFixedInjectionKey, isLastColFixedInjectionKey, isColsResizableInjectionKey } from "./p-table.js";
|
|
45
46
|
import { MIN_WIDTH_COL_RESIZE } from "./p-table.js";
|
|
46
47
|
import { usePTableColResize } from "./usePTableColResize.js";
|
|
@@ -49,10 +50,10 @@ import PTableTd from "./p-table-td.js";
|
|
|
49
50
|
import { _ as _export_sfc } from "./chunks/_plugin-vue_export-helper.js";
|
|
50
51
|
import { usePTableRowVirtualizer } from "./usePTableRowVirtualizer.js";
|
|
51
52
|
import { default as default11 } from "./p-table-filter-icon.js";
|
|
52
|
-
import { _ as
|
|
53
|
+
import { _ as _20 } from "./chunks/p-table-loader.js";
|
|
53
54
|
import { SORTING_TYPES } from "./p-table-sort.js";
|
|
54
|
-
import { _ as
|
|
55
|
-
import { _ as
|
|
55
|
+
import { _ as _21 } from "./chunks/p-tabs.js";
|
|
56
|
+
import { _ as _22 } from "./chunks/p-textarea.js";
|
|
56
57
|
import { default as default12 } from "./p-toggle.js";
|
|
57
58
|
import { squirrelTailwindConfig } from "./config.js";
|
|
58
59
|
import { S } from "./chunks/p-btn.types.js";
|
|
@@ -1018,13 +1019,14 @@ export {
|
|
|
1018
1019
|
_sfc_main$2 as PSelectList,
|
|
1019
1020
|
default9 as PSelectPill,
|
|
1020
1021
|
default10 as PSkeletonLoader,
|
|
1022
|
+
_19 as PSteps,
|
|
1021
1023
|
pTable as PTable,
|
|
1022
1024
|
PTableHeaderCell,
|
|
1023
|
-
|
|
1025
|
+
_20 as PTableLoader,
|
|
1024
1026
|
_sfc_main as PTableSort,
|
|
1025
1027
|
PTableTd,
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
+
_21 as PTabs,
|
|
1029
|
+
_22 as PTextarea,
|
|
1028
1030
|
default12 as PToggle,
|
|
1029
1031
|
P_ICON_ALIASES,
|
|
1030
1032
|
S as SIZES,
|
|
@@ -37,6 +37,7 @@ import PSelectList from './p-select-list/p-select-list.vue';
|
|
|
37
37
|
import { useSelectList } from './p-select-list/useSelectList';
|
|
38
38
|
import PSelectPill from './p-select-pill/p-select-pill.vue';
|
|
39
39
|
import PSkeletonLoader from './p-skeleton-loader/p-skeleton-loader.vue';
|
|
40
|
+
import PSteps from './p-steps/p-steps.vue';
|
|
40
41
|
import { colsInjectionKey, type HeaderCellAttrs, isColsResizableInjectionKey, isFirstColFixedInjectionKey, isLastColFixedInjectionKey, MIN_WIDTH_COL_RESIZE, type TableCol, type ThAttrs } from './p-table/p-table.types';
|
|
41
42
|
import PTable from './p-table/p-table.vue';
|
|
42
43
|
import { usePTableColResize } from './p-table/usePTableColResize';
|
|
@@ -50,4 +51,4 @@ import PTableTd from './p-table-td/p-table-td.vue';
|
|
|
50
51
|
import PTabs from './p-tabs/p-tabs.vue';
|
|
51
52
|
import PTextarea from './p-textarea/p-textarea.vue';
|
|
52
53
|
import PToggle from './p-toggle/p-toggle.vue';
|
|
53
|
-
export { colsInjectionKey, FileUploadFile, HeaderCellAttrs, isColsResizableInjectionKey, isFirstColFixedInjectionKey, isLastColFixedInjectionKey, MIN_WIDTH_COL_RESIZE, P_ICON_ALIASES, PActionBar, PActionBarAction, PAlert, PAvatar, PBtn, PCard, PCheckbox, PChips, PCloseBtn, PDatePicker, PDrawer, PDropdown, PDropdownSelect, PFileUpload, PFilterIcon, PIcon, PIconAlias, PInfoIcon, PInlineDatePicker, PInput, PInputNumber, PInputPercent, PInputSearch, PLink, PLoading, PModal, PPagination, PPaginationInfo, PProgressBar, PRingLoader, PSelect, PSelectBtn, PSelectList, PSelectPill, PSkeletonLoader, PTable, PTableHeaderCell, PTableLoader, PTableSort, PTableTd, PTabs, PTextarea, PToggle, Size, SORTING_TYPES, SortingType, SortingTypeWithoutNoSorting, TableCol, ThAttrs, usePLoading, usePModal, usePTableColResize, usePTableRowVirtualizer, useSelectList, };
|
|
54
|
+
export { colsInjectionKey, FileUploadFile, HeaderCellAttrs, isColsResizableInjectionKey, isFirstColFixedInjectionKey, isLastColFixedInjectionKey, MIN_WIDTH_COL_RESIZE, P_ICON_ALIASES, PActionBar, PActionBarAction, PAlert, PAvatar, PBtn, PCard, PCheckbox, PChips, PCloseBtn, PDatePicker, PDrawer, PDropdown, PDropdownSelect, PFileUpload, PFilterIcon, PIcon, PIconAlias, PInfoIcon, PInlineDatePicker, PInput, PInputNumber, PInputPercent, PInputSearch, PLink, PLoading, PModal, PPagination, PPaginationInfo, PProgressBar, PRingLoader, PSelect, PSelectBtn, PSelectList, PSelectPill, PSkeletonLoader, PSteps, PTable, PTableHeaderCell, PTableLoader, PTableSort, PTableTd, PTabs, PTextarea, PToggle, Size, SORTING_TYPES, SortingType, SortingTypeWithoutNoSorting, TableCol, ThAttrs, usePLoading, usePModal, usePTableColResize, usePTableRowVirtualizer, useSelectList, };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
declare const _default: <T extends readonly string[]>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
2
|
+
props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, never>, never> & {
|
|
3
|
+
steps: T;
|
|
4
|
+
currentStep: T[number];
|
|
5
|
+
stepTitleMap?: Partial<Record<T[number], string>>;
|
|
6
|
+
} & Partial<{}>> & import("vue").PublicProps;
|
|
7
|
+
expose(exposed: import("vue").ShallowUnwrapRef<{}>): void;
|
|
8
|
+
attrs: any;
|
|
9
|
+
slots: {};
|
|
10
|
+
emit: {};
|
|
11
|
+
}>) => import("vue").VNode & {
|
|
12
|
+
__ctx?: Awaited<typeof __VLS_setup>;
|
|
13
|
+
};
|
|
14
|
+
export default _default;
|
|
15
|
+
type __VLS_PrettifyLocal<T> = {
|
|
16
|
+
[K in keyof T]: T[K];
|
|
17
|
+
} & {};
|
package/package.json
CHANGED
|
@@ -37,6 +37,7 @@ import PSelectList from '@squirrel/components/p-select-list/p-select-list.vue';
|
|
|
37
37
|
import { useSelectList } from '@squirrel/components/p-select-list/useSelectList';
|
|
38
38
|
import PSelectPill from '@squirrel/components/p-select-pill/p-select-pill.vue';
|
|
39
39
|
import PSkeletonLoader from '@squirrel/components/p-skeleton-loader/p-skeleton-loader.vue';
|
|
40
|
+
import PSteps from '@squirrel/components/p-steps/p-steps.vue';
|
|
40
41
|
import {
|
|
41
42
|
colsInjectionKey,
|
|
42
43
|
type HeaderCellAttrs,
|
|
@@ -108,6 +109,7 @@ export {
|
|
|
108
109
|
PSelectList,
|
|
109
110
|
PSelectPill,
|
|
110
111
|
PSkeletonLoader,
|
|
112
|
+
PSteps,
|
|
111
113
|
PTable,
|
|
112
114
|
PTableHeaderCell,
|
|
113
115
|
PTableLoader,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`PSteps.vue > renders correctly 1`] = `
|
|
4
|
+
"<div class="flex items-center gap-2">
|
|
5
|
+
<div class="text-nowrap rounded-full border px-4 py-1 text-sm font-semibold border border-p-blue-50 text-p-blue-50">First</div>
|
|
6
|
+
<div class="flex items-center">
|
|
7
|
+
<iconify-icon icon="material-symbols:arrow-right-alt-rounded" class="text-p-blue-50"></iconify-icon>
|
|
8
|
+
</div>
|
|
9
|
+
<div class="text-nowrap rounded-full border px-4 py-1 text-sm font-semibold border border-p-blue-50 bg-p-blue-50 text-surface">Second</div>
|
|
10
|
+
<div class="flex items-center">
|
|
11
|
+
<iconify-icon icon="material-symbols:arrow-right-alt-rounded" class="text-p-gray-30"></iconify-icon>
|
|
12
|
+
</div>
|
|
13
|
+
<div class="text-nowrap rounded-full border px-4 py-1 text-sm font-semibold border border-p-gray-30 text-p-gray-30">Third</div>
|
|
14
|
+
<!--v-if-->
|
|
15
|
+
</div>"
|
|
16
|
+
`;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import PSteps from '@squirrel/components/p-steps/p-steps.vue';
|
|
2
|
+
import { createWrapperFor } from '@tests/vitest.helpers';
|
|
3
|
+
|
|
4
|
+
describe('PSteps.vue', () => {
|
|
5
|
+
it('renders correctly', () => {
|
|
6
|
+
const wrapper = createWrapperFor(PSteps, {
|
|
7
|
+
props: {
|
|
8
|
+
steps: ['first', 'second', 'third'],
|
|
9
|
+
currentStep: 'second',
|
|
10
|
+
stepTitleMap: {},
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
expect(wrapper.html()).toMatchSnapshot();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('renders the correct number of steps', () => {
|
|
17
|
+
const steps = ['first', 'second', 'third'];
|
|
18
|
+
const wrapper = createWrapperFor(PSteps, {
|
|
19
|
+
props: {
|
|
20
|
+
steps,
|
|
21
|
+
currentStep: 'second',
|
|
22
|
+
stepTitleMap: {},
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const stepElements = wrapper.findAll('.rounded-full.border');
|
|
27
|
+
expect(stepElements.length).toBe(steps.length);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('applies correct classes for current step', () => {
|
|
31
|
+
const wrapper = createWrapperFor(PSteps, {
|
|
32
|
+
props: {
|
|
33
|
+
steps: ['first', 'second', 'third'],
|
|
34
|
+
currentStep: 'second',
|
|
35
|
+
stepTitleMap: {},
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const stepElements = wrapper.findAll('.rounded-full.border');
|
|
40
|
+
|
|
41
|
+
// First step should be completed (blue text)
|
|
42
|
+
expect(stepElements[0].classes()).toContain('text-nowrap');
|
|
43
|
+
expect(stepElements[0].classes()).toContain('text-p-blue-50');
|
|
44
|
+
expect(stepElements[0].classes()).toContain('border-p-blue-50');
|
|
45
|
+
|
|
46
|
+
// Second step should be current (blue background)
|
|
47
|
+
expect(stepElements[1].classes()).toContain('text-nowrap');
|
|
48
|
+
expect(stepElements[1].classes()).toContain('bg-p-blue-50');
|
|
49
|
+
expect(stepElements[1].classes()).toContain('text-surface');
|
|
50
|
+
expect(stepElements[1].classes()).toContain('border-p-blue-50');
|
|
51
|
+
|
|
52
|
+
// Third step should be upcoming (gray)
|
|
53
|
+
expect(stepElements[2].classes()).toContain('text-nowrap');
|
|
54
|
+
expect(stepElements[2].classes()).toContain('text-p-gray-30');
|
|
55
|
+
expect(stepElements[2].classes()).toContain('border-p-gray-30');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('displays step titles from stepTitleMap when provided', () => {
|
|
59
|
+
const wrapper = createWrapperFor(PSteps, {
|
|
60
|
+
props: {
|
|
61
|
+
steps: ['step1', 'step2', 'step3'],
|
|
62
|
+
currentStep: 'step2',
|
|
63
|
+
stepTitleMap: {
|
|
64
|
+
step1: 'Custom Step 1',
|
|
65
|
+
step2: 'Custom Step 2',
|
|
66
|
+
step3: 'Custom Step 3',
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const stepElements = wrapper.findAll('.rounded-full.border');
|
|
72
|
+
expect(stepElements[0].text()).toBe('Custom Step 1');
|
|
73
|
+
expect(stepElements[1].text()).toBe('Custom Step 2');
|
|
74
|
+
expect(stepElements[2].text()).toBe('Custom Step 3');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('uses startCase for step titles when stepTitleMap entry is not provided', () => {
|
|
78
|
+
const wrapper = createWrapperFor(PSteps, {
|
|
79
|
+
props: {
|
|
80
|
+
steps: ['firstStep', 'secondStep', 'thirdStep'],
|
|
81
|
+
currentStep: 'secondStep',
|
|
82
|
+
stepTitleMap: {
|
|
83
|
+
secondStep: 'Custom Second',
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const stepElements = wrapper.findAll('.rounded-full.border');
|
|
89
|
+
expect(stepElements[0].text()).toBe('First Step'); // startCase applied
|
|
90
|
+
expect(stepElements[1].text()).toBe('Custom Second'); // from map
|
|
91
|
+
expect(stepElements[2].text()).toBe('Third Step'); // startCase applied
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('renders the correct number of arrows between steps', () => {
|
|
95
|
+
const wrapper = createWrapperFor(PSteps, {
|
|
96
|
+
props: {
|
|
97
|
+
steps: ['first', 'second', 'third', 'fourth'],
|
|
98
|
+
currentStep: 'second',
|
|
99
|
+
stepTitleMap: {},
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// There should be 3 arrows for 4 steps
|
|
104
|
+
const arrowElements = wrapper.findAll('[icon="material-symbols:arrow-right-alt-rounded"]');
|
|
105
|
+
expect(arrowElements.length).toBe(3);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('applies the correct classes to arrows based on current step', () => {
|
|
109
|
+
const wrapper = createWrapperFor(PSteps, {
|
|
110
|
+
props: {
|
|
111
|
+
steps: ['first', 'second', 'third', 'fourth'],
|
|
112
|
+
currentStep: 'second',
|
|
113
|
+
stepTitleMap: {},
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const arrowElements = wrapper.findAll('[icon="material-symbols:arrow-right-alt-rounded"]');
|
|
118
|
+
|
|
119
|
+
// Arrow between first and second step should be colored
|
|
120
|
+
expect(arrowElements[0].classes()).toContain('text-p-blue-50');
|
|
121
|
+
|
|
122
|
+
// Arrow after current step should be gray
|
|
123
|
+
expect(arrowElements[1].classes()).toContain('text-p-gray-30');
|
|
124
|
+
expect(arrowElements[2].classes()).toContain('text-p-gray-30');
|
|
125
|
+
});
|
|
126
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import PSteps from '@squirrel/components/p-steps/p-steps.vue';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
title: 'Components/PSteps',
|
|
5
|
+
component: PSteps,
|
|
6
|
+
tags: ['autodocs'],
|
|
7
|
+
parameters: {
|
|
8
|
+
docs: {
|
|
9
|
+
description: {
|
|
10
|
+
component: 'Steps Component to be used in wizards',
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const Default = {
|
|
17
|
+
render: (args) => ({
|
|
18
|
+
components: { PSteps },
|
|
19
|
+
setup() {
|
|
20
|
+
return { args };
|
|
21
|
+
},
|
|
22
|
+
template: `<PSteps v-bind="args" />`,
|
|
23
|
+
}),
|
|
24
|
+
args: {
|
|
25
|
+
steps: ['stepOne', 'stepTwo', 'stepThree', 'stepFour'],
|
|
26
|
+
currentStep: 'stepTwo',
|
|
27
|
+
stepTitleMap: {
|
|
28
|
+
stepTwo: 'Criteria',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flex items-center gap-2">
|
|
3
|
+
<template v-for="(step, idx) in steps" :key="step">
|
|
4
|
+
<div class="text-nowrap rounded-full border px-4 py-1 text-sm font-semibold" :class="stepClasses(step, idx)">
|
|
5
|
+
{{ stepTitle(step) }}
|
|
6
|
+
</div>
|
|
7
|
+
<div v-if="idx < steps.length - 1" class="flex items-center">
|
|
8
|
+
<PIcon
|
|
9
|
+
icon="material-symbols:arrow-right-alt-rounded"
|
|
10
|
+
:class="[currentStepIndex <= idx ? 'text-p-gray-30' : 'text-p-blue-50']"
|
|
11
|
+
/>
|
|
12
|
+
</div>
|
|
13
|
+
</template>
|
|
14
|
+
</div>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<script setup lang="ts" generic="T extends readonly string[]">
|
|
18
|
+
import PIcon from '@squirrel/components/p-icon/p-icon.vue';
|
|
19
|
+
import { startCase } from 'lodash-es';
|
|
20
|
+
import { computed } from 'vue';
|
|
21
|
+
|
|
22
|
+
type Props = {
|
|
23
|
+
steps: T;
|
|
24
|
+
currentStep: T[number];
|
|
25
|
+
stepTitleMap?: Partial<Record<T[number], string>>;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const props = defineProps<Props>();
|
|
29
|
+
|
|
30
|
+
const currentStepIndex = computed(() => props.steps.findIndex((s) => s === props.currentStep));
|
|
31
|
+
|
|
32
|
+
const stepClasses = (step: T[number], stepIndex: number) => {
|
|
33
|
+
if (step === props.currentStep) {
|
|
34
|
+
return 'border border-p-blue-50 bg-p-blue-50 text-surface';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (currentStepIndex.value < stepIndex) {
|
|
38
|
+
return 'border border-p-gray-30 text-p-gray-30';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return 'border border-p-blue-50 text-p-blue-50';
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const stepTitle = (step: T[number]) => {
|
|
45
|
+
return props.stepTitleMap?.[step] || startCase(step);
|
|
46
|
+
};
|
|
47
|
+
</script>
|