@bethinkpl/design-system 44.0.0 → 45.0.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/design-system.css +1 -1
- package/dist/design-system.js +9623 -9592
- package/dist/design-system.js.map +1 -1
- package/dist/lib/js/components/Buttons/IconButton/IconButton.vue.d.ts +1 -1
- package/dist/lib/js/components/Callout/Callout.consts.d.ts +32 -0
- package/dist/lib/js/components/{ActionContent/ActionContent.vue.d.ts → Callout/Callout.vue.d.ts} +17 -6
- package/dist/lib/js/components/Callout/index.d.ts +4 -0
- package/dist/lib/js/components/DatePickers/DateBox/DateBox.vue.d.ts +1 -1
- package/dist/lib/js/components/DatePickers/DatePicker/DatePicker.vue.d.ts +5 -5
- package/dist/lib/js/components/DatePickers/DateRangePicker/DateRangePicker.vue.d.ts +2 -2
- package/dist/lib/js/components/Divider/Divider.vue.d.ts +1 -1
- package/dist/lib/js/components/Drawer/DrawerDivider/DrawerDivider.vue.d.ts +1 -1
- package/dist/lib/js/components/Drawer/DrawerSection/DrawerSection.vue.d.ts +4 -4
- package/dist/lib/js/components/Drawer/DrawerTile/DrawerTile.vue.d.ts +4 -4
- package/dist/lib/js/components/Dropdown/Dropdown.vue.d.ts +1 -1
- package/dist/lib/js/components/Form/FormField/FormField.utils.d.ts +1 -1
- package/dist/lib/js/components/Form/InputField/InputField.vue.d.ts +2 -2
- package/dist/lib/js/components/Form/PasswordField/PasswordField.vue.d.ts +2 -2
- package/dist/lib/js/components/Form/SelectField/SelectField.vue.d.ts +2 -2
- package/dist/lib/js/components/Form/TextAreaField/TextAreaField.vue.d.ts +2 -2
- package/dist/lib/js/components/Headers/OverlayHeader/OverlayHeader.vue.d.ts +5 -5
- package/dist/lib/js/components/Headers/PageHeader/PageHeader.vue.d.ts +1 -1
- package/dist/lib/js/components/Headers/SectionHeader/SectionHeader.vue.d.ts +3 -3
- package/dist/lib/js/components/IconText/IconText.vue.d.ts +1 -1
- package/dist/lib/js/components/Image/Image.vue.d.ts +1 -1
- package/dist/lib/js/components/Menu/MenuDivider/MenuDivider.vue.d.ts +1 -1
- package/dist/lib/js/components/Modals/Modal/Modal.vue.d.ts +2 -2
- package/dist/lib/js/components/Modals/ModalDialog/ModalDialog.vue.d.ts +2 -2
- package/dist/lib/js/components/Pagination/Pagination.vue.d.ts +2 -2
- package/dist/lib/js/components/RichList/BasicRichListItem/BasicRichListItem.vue.d.ts +7 -7
- package/dist/lib/js/components/RichList/RichListItem/RichListItem.vue.d.ts +3 -3
- package/dist/lib/js/components/SelectList/SelectListItemDivider/SelectListItemDivider.vue.d.ts +1 -1
- package/dist/lib/js/components/Skeleton/Skeleton.vue.d.ts +1 -1
- package/dist/lib/js/components/SurveyQuestions/SurveyQuestionOpenEnded/SurveyQuestionOpenEnded.vue.d.ts +3 -3
- package/dist/lib/js/components/SurveyQuestions/SurveyQuestionScale/SurveyQuestionScale.vue.d.ts +3 -3
- package/dist/lib/js/components/Tile/Tile.sb.shared.d.ts +2 -2
- package/dist/lib/js/components/Tile/Tile.vue.d.ts +2 -2
- package/dist/lib/js/components/Toggles/ToggleButton/ToggleButton.vue.d.ts +1 -1
- package/dist/lib/js/index.d.ts +2 -1
- package/lib/js/components/Callout/Callout.consts.ts +25 -0
- package/lib/js/components/Callout/Callout.spec.ts +257 -0
- package/lib/js/components/Callout/Callout.stories.ts +209 -0
- package/lib/js/components/Callout/Callout.vue +161 -0
- package/lib/js/components/Callout/index.ts +4 -0
- package/lib/js/components/Cards/Card/Card.vue +1 -1
- package/lib/js/components/Form/PasswordField/PasswordField.vue +1 -1
- package/lib/js/index.ts +2 -1
- package/package.json +1 -1
- package/dist/lib/js/components/ActionContent/index.d.ts +0 -3
- package/lib/js/components/ActionContent/ActionContent.spec.ts +0 -99
- package/lib/js/components/ActionContent/ActionContent.stories.ts +0 -141
- package/lib/js/components/ActionContent/ActionContent.vue +0 -104
- package/lib/js/components/ActionContent/index.ts +0 -3
|
@@ -1046,9 +1046,9 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
|
|
|
1046
1046
|
type: string;
|
|
1047
1047
|
touchableDeprecated: boolean;
|
|
1048
1048
|
color: string;
|
|
1049
|
+
elevation: string;
|
|
1049
1050
|
radius: string;
|
|
1050
1051
|
state: string;
|
|
1051
|
-
elevation: string;
|
|
1052
1052
|
colorScheme: string;
|
|
1053
1053
|
}, {}, {
|
|
1054
1054
|
WnlIcon: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
|
|
@@ -1655,7 +1655,7 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
|
|
|
1655
1655
|
};
|
|
1656
1656
|
}, {}, {
|
|
1657
1657
|
calcHeaderFeatureIconColor(): any;
|
|
1658
|
-
calcFooterPrimaryButtonColor(): "
|
|
1658
|
+
calcFooterPrimaryButtonColor(): "primary" | "neutral";
|
|
1659
1659
|
calcFooterSecondaryButtonColor(): "danger" | "neutral";
|
|
1660
1660
|
calcSingleColumn(): boolean;
|
|
1661
1661
|
displayFooter(): any;
|
|
@@ -2351,9 +2351,9 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
|
|
|
2351
2351
|
type: string;
|
|
2352
2352
|
touchableDeprecated: boolean;
|
|
2353
2353
|
color: string;
|
|
2354
|
+
elevation: string;
|
|
2354
2355
|
radius: string;
|
|
2355
2356
|
state: string;
|
|
2356
|
-
elevation: string;
|
|
2357
2357
|
colorScheme: string;
|
|
2358
2358
|
}, {}, {
|
|
2359
2359
|
WnlIcon: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
|
package/dist/lib/js/components/SurveyQuestions/SurveyQuestionScale/SurveyQuestionScale.vue.d.ts
CHANGED
|
@@ -1107,9 +1107,9 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
|
|
|
1107
1107
|
type: string;
|
|
1108
1108
|
touchableDeprecated: boolean;
|
|
1109
1109
|
color: string;
|
|
1110
|
+
elevation: string;
|
|
1110
1111
|
radius: string;
|
|
1111
1112
|
state: string;
|
|
1112
|
-
elevation: string;
|
|
1113
1113
|
colorScheme: string;
|
|
1114
1114
|
}, {}, {
|
|
1115
1115
|
WnlIcon: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
|
|
@@ -1889,7 +1889,7 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
|
|
|
1889
1889
|
};
|
|
1890
1890
|
}, {}, {
|
|
1891
1891
|
calcHeaderFeatureIconColor(): any;
|
|
1892
|
-
calcFooterPrimaryButtonColor(): "
|
|
1892
|
+
calcFooterPrimaryButtonColor(): "primary" | "neutral";
|
|
1893
1893
|
calcFooterSecondaryButtonColor(): "danger" | "neutral";
|
|
1894
1894
|
calcSingleColumn(): boolean;
|
|
1895
1895
|
displayFooter(): any;
|
|
@@ -2585,9 +2585,9 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
|
|
|
2585
2585
|
type: string;
|
|
2586
2586
|
touchableDeprecated: boolean;
|
|
2587
2587
|
color: string;
|
|
2588
|
+
elevation: string;
|
|
2588
2589
|
radius: string;
|
|
2589
2590
|
state: string;
|
|
2590
|
-
elevation: string;
|
|
2591
2591
|
colorScheme: string;
|
|
2592
2592
|
}, {}, {
|
|
2593
2593
|
WnlIcon: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
|
|
@@ -299,8 +299,8 @@ export declare const data: () => {
|
|
|
299
299
|
readonly ACCENT: "accent";
|
|
300
300
|
}>;
|
|
301
301
|
allowedColorsToBorderColorsMap: {
|
|
302
|
-
neutral: ("
|
|
303
|
-
neutralWeak: ("
|
|
302
|
+
neutral: ("primary" | "neutral" | "neutralWeak" | "primaryWeak")[];
|
|
303
|
+
neutralWeak: ("primary" | "neutral" | "neutralWeak" | "primaryWeak")[];
|
|
304
304
|
primary: ("primary" | "primaryWeak")[];
|
|
305
305
|
success: ("success" | "successWeak")[];
|
|
306
306
|
fail: ("fail" | "failWeak")[];
|
|
@@ -126,11 +126,11 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
|
|
|
126
126
|
};
|
|
127
127
|
}>> & Readonly<{}>, {
|
|
128
128
|
color: import('./Tile.consts').TileColor;
|
|
129
|
-
eyebrowText: string;
|
|
130
|
-
isEyebrowTextUppercase: boolean;
|
|
131
129
|
state: import('./Tile.consts').TileState;
|
|
132
130
|
iconLeft: Record<string, any>;
|
|
133
131
|
iconRight: Record<string, any>;
|
|
132
|
+
eyebrowText: string;
|
|
133
|
+
isEyebrowTextUppercase: boolean;
|
|
134
134
|
interactive: boolean;
|
|
135
135
|
additionalText: string;
|
|
136
136
|
eyebrowEllipsis: boolean;
|
|
@@ -433,10 +433,10 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
|
|
|
433
433
|
label: string;
|
|
434
434
|
color: ToggleButtonColor;
|
|
435
435
|
radius: ToggleButtonRadius;
|
|
436
|
-
isInteractive: boolean;
|
|
437
436
|
state: ToggleButtonState;
|
|
438
437
|
iconLeft: any;
|
|
439
438
|
iconRight: any;
|
|
439
|
+
isInteractive: boolean;
|
|
440
440
|
isLabelUppercase: boolean;
|
|
441
441
|
isSelected: boolean;
|
|
442
442
|
hasSmallHorizontalPadding: boolean;
|
package/dist/lib/js/index.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export { default as DsActionContent } from './components/ActionContent';
|
|
2
1
|
export { default as Avatar } from './components/Avatar';
|
|
3
2
|
export * from './components/Avatar/Avatar.consts';
|
|
4
3
|
export { default as Badge } from './components/Badge';
|
|
@@ -15,6 +14,8 @@ export * from './components/Buttons/Button/Button.consts';
|
|
|
15
14
|
export { default as ToggleButton } from './components/Toggles/ToggleButton';
|
|
16
15
|
export { default as DsToggleButton } from './components/Toggles/ToggleButton';
|
|
17
16
|
export * from './components/Toggles/ToggleButton/ToggleButton.consts';
|
|
17
|
+
export { default as DsCallout } from './components/Callout';
|
|
18
|
+
export * from './components/Callout/Callout.consts';
|
|
18
19
|
export { default as Card } from './components/Cards/Card/';
|
|
19
20
|
export * from './components/Cards/Card';
|
|
20
21
|
export { default as DsCard } from './components/Cards/Card';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Value } from '../../utils/type.utils';
|
|
2
|
+
import { FEATURE_ICON_COLOR, FeatureIconColor } from '../Icons/FeatureIcon/FeatureIcon.consts';
|
|
3
|
+
import { TEXT_GROUP_MAIN_TEXT_COLORS, TextGroupMainTextColor } from '../TextGroup/TextGroup.consts';
|
|
4
|
+
|
|
5
|
+
export const CALLOUT_LAYOUTS = {
|
|
6
|
+
VERTICAL: 'vertical',
|
|
7
|
+
HORIZONTAL: 'horizontal',
|
|
8
|
+
} as const;
|
|
9
|
+
|
|
10
|
+
export type CalloutLayout = Value<typeof CALLOUT_LAYOUTS>;
|
|
11
|
+
|
|
12
|
+
export const CALLOUT_SIZES = {
|
|
13
|
+
SMALL: 'small',
|
|
14
|
+
MEDIUM: 'medium',
|
|
15
|
+
LARGE: 'large',
|
|
16
|
+
} as const;
|
|
17
|
+
|
|
18
|
+
export type CalloutSize = Value<typeof CALLOUT_SIZES>;
|
|
19
|
+
|
|
20
|
+
// Aliased so the sets can never drift from the components they are forwarded to.
|
|
21
|
+
export const CALLOUT_ICON_COLORS = FEATURE_ICON_COLOR;
|
|
22
|
+
export type CalloutIconColor = FeatureIconColor;
|
|
23
|
+
|
|
24
|
+
export const CALLOUT_MAIN_TEXT_COLORS = TEXT_GROUP_MAIN_TEXT_COLORS;
|
|
25
|
+
export type CalloutMainTextColor = TextGroupMainTextColor;
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { mount } from '@vue/test-utils';
|
|
3
|
+
import { ComponentProps } from 'vue-component-type-helpers';
|
|
4
|
+
import { h } from 'vue';
|
|
5
|
+
import Callout from './Callout.vue';
|
|
6
|
+
import {
|
|
7
|
+
CALLOUT_ICON_COLORS,
|
|
8
|
+
CALLOUT_LAYOUTS,
|
|
9
|
+
CALLOUT_MAIN_TEXT_COLORS,
|
|
10
|
+
CALLOUT_SIZES,
|
|
11
|
+
} from './Callout.consts';
|
|
12
|
+
import DsFeatureIcon from '../Icons/FeatureIcon';
|
|
13
|
+
import { FEATURE_ICON_SIZES } from '../Icons/FeatureIcon/FeatureIcon.consts';
|
|
14
|
+
import DsTextGroup from '../TextGroup/TextGroup.vue';
|
|
15
|
+
import { TEXT_GROUP_ALIGNS, TEXT_GROUP_SIZES } from '../TextGroup/TextGroup.consts';
|
|
16
|
+
import DsButton from '../Buttons/Button';
|
|
17
|
+
import { BUTTON_COLORS, BUTTON_RADIUSES, BUTTON_SIZES, BUTTON_TYPES } from '../Buttons/Button';
|
|
18
|
+
import { ICONS } from '../Icons/Icon';
|
|
19
|
+
|
|
20
|
+
type MountOptions = Omit<NonNullable<Parameters<typeof mount<typeof Callout>>[1]>, 'props'> & {
|
|
21
|
+
props?: Partial<ComponentProps<typeof Callout>>;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const mountCallout = (options: MountOptions = {}) =>
|
|
25
|
+
mount(Callout, {
|
|
26
|
+
...options,
|
|
27
|
+
props: { icon: ICONS.FA_COMMENT_DOTS, ...options.props },
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('Callout', () => {
|
|
31
|
+
it('renders', () => {
|
|
32
|
+
const wrapper = mountCallout();
|
|
33
|
+
|
|
34
|
+
expect(wrapper.exists()).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('renders the feature icon', () => {
|
|
38
|
+
const wrapper = mountCallout();
|
|
39
|
+
|
|
40
|
+
expect(wrapper.findComponent(DsFeatureIcon).exists()).toBe(true);
|
|
41
|
+
expect(wrapper.findComponent(DsFeatureIcon).props('icon')).toStrictEqual(
|
|
42
|
+
ICONS.FA_COMMENT_DOTS,
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('passes main and supporting text to the text group', () => {
|
|
47
|
+
const wrapper = mountCallout({
|
|
48
|
+
props: {
|
|
49
|
+
mainText: 'Main text',
|
|
50
|
+
supportingText: 'Supporting text',
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
expect(wrapper.find('.ds-callout__text').text()).toContain('Main text');
|
|
55
|
+
expect(wrapper.find('.ds-callout__text').text()).toContain('Supporting text');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('layout', () => {
|
|
59
|
+
it.each(Object.values(CALLOUT_LAYOUTS))('applies the layout class for "%s"', (layout) => {
|
|
60
|
+
const wrapper = mountCallout({ props: { layout } });
|
|
61
|
+
|
|
62
|
+
expect(wrapper.find('.ds-callout').classes()).toContain(`-ds-layout-${layout}`);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('is vertical by default', () => {
|
|
66
|
+
const wrapper = mountCallout();
|
|
67
|
+
|
|
68
|
+
expect(wrapper.find('.ds-callout').classes()).toContain('-ds-layout-vertical');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it.each([
|
|
72
|
+
[CALLOUT_LAYOUTS.VERTICAL, TEXT_GROUP_ALIGNS.CENTER],
|
|
73
|
+
[CALLOUT_LAYOUTS.HORIZONTAL, TEXT_GROUP_ALIGNS.LEFT],
|
|
74
|
+
])('aligns the text group for layout "%s"', (layout, expectedAlign) => {
|
|
75
|
+
const wrapper = mountCallout({ props: { layout } });
|
|
76
|
+
|
|
77
|
+
expect(wrapper.findComponent(DsTextGroup).props('align')).toBe(expectedAlign);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('size', () => {
|
|
82
|
+
it('is medium by default', () => {
|
|
83
|
+
const wrapper = mountCallout();
|
|
84
|
+
|
|
85
|
+
expect(wrapper.findComponent(DsFeatureIcon).props('size')).toBe(
|
|
86
|
+
FEATURE_ICON_SIZES.MEDIUM,
|
|
87
|
+
);
|
|
88
|
+
expect(wrapper.findComponent(DsTextGroup).props('size')).toBe(TEXT_GROUP_SIZES.LARGE);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it.each([
|
|
92
|
+
[CALLOUT_SIZES.SMALL, FEATURE_ICON_SIZES.MEDIUM, TEXT_GROUP_SIZES.MEDIUM],
|
|
93
|
+
[CALLOUT_SIZES.MEDIUM, FEATURE_ICON_SIZES.MEDIUM, TEXT_GROUP_SIZES.LARGE],
|
|
94
|
+
[CALLOUT_SIZES.LARGE, FEATURE_ICON_SIZES.LARGE, TEXT_GROUP_SIZES.LARGE],
|
|
95
|
+
])(
|
|
96
|
+
'maps size "%s" to feature icon "%s" and text group "%s"',
|
|
97
|
+
(size, featureIconSize, textGroupSize) => {
|
|
98
|
+
const wrapper = mountCallout({ props: { size } });
|
|
99
|
+
|
|
100
|
+
expect(wrapper.findComponent(DsFeatureIcon).props('size')).toBe(featureIconSize);
|
|
101
|
+
expect(wrapper.findComponent(DsTextGroup).props('size')).toBe(textGroupSize);
|
|
102
|
+
},
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe('icon color', () => {
|
|
107
|
+
it.each(Object.values(CALLOUT_ICON_COLORS))(
|
|
108
|
+
'passes icon color "%s" to the feature icon',
|
|
109
|
+
(iconColor) => {
|
|
110
|
+
const wrapper = mountCallout({ props: { iconColor } });
|
|
111
|
+
|
|
112
|
+
expect(wrapper.findComponent(DsFeatureIcon).props('color')).toBe(iconColor);
|
|
113
|
+
},
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
it('is primary by default', () => {
|
|
117
|
+
const wrapper = mountCallout();
|
|
118
|
+
|
|
119
|
+
expect(wrapper.findComponent(DsFeatureIcon).props('color')).toBe(
|
|
120
|
+
CALLOUT_ICON_COLORS.PRIMARY,
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe('main text color', () => {
|
|
126
|
+
it.each(Object.values(CALLOUT_MAIN_TEXT_COLORS))(
|
|
127
|
+
'passes main text color "%s" to the text group',
|
|
128
|
+
(mainTextColor) => {
|
|
129
|
+
const wrapper = mountCallout({ props: { mainTextColor } });
|
|
130
|
+
|
|
131
|
+
expect(wrapper.findComponent(DsTextGroup).props('mainTextColor')).toBe(
|
|
132
|
+
mainTextColor,
|
|
133
|
+
);
|
|
134
|
+
},
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
it('is primary by default', () => {
|
|
138
|
+
const wrapper = mountCallout();
|
|
139
|
+
|
|
140
|
+
expect(wrapper.findComponent(DsTextGroup).props('mainTextColor')).toBe(
|
|
141
|
+
CALLOUT_MAIN_TEXT_COLORS.PRIMARY,
|
|
142
|
+
);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
describe('eyebrow text', () => {
|
|
147
|
+
it('does not render the eyebrow by default', () => {
|
|
148
|
+
const wrapper = mountCallout();
|
|
149
|
+
|
|
150
|
+
expect(wrapper.findComponent(DsTextGroup).props('eyebrowText')).toBe(null);
|
|
151
|
+
expect(wrapper.find('.ds-textGroup__eyebrow').exists()).toBe(false);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('renders the eyebrow when eyebrowText is provided', () => {
|
|
155
|
+
const wrapper = mountCallout({ props: { eyebrowText: 'Eyebrow text' } });
|
|
156
|
+
|
|
157
|
+
expect(wrapper.find('.ds-textGroup__eyebrow').text()).toBe('Eyebrow text');
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('never uppercases the eyebrow', () => {
|
|
161
|
+
const wrapper = mountCallout({ props: { eyebrowText: 'Eyebrow text' } });
|
|
162
|
+
|
|
163
|
+
expect(wrapper.findComponent(DsTextGroup).props('isEyebrowTextUppercase')).toBe(false);
|
|
164
|
+
expect(wrapper.find('.ds-textGroup__eyebrow').classes()).not.toContain('-ds-uppercase');
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
describe('actions', () => {
|
|
169
|
+
it('renders a default primary button with the button label and icon', () => {
|
|
170
|
+
const wrapper = mountCallout({
|
|
171
|
+
props: {
|
|
172
|
+
buttonLabel: 'Confirm',
|
|
173
|
+
buttonIcon: ICONS.FA_COMMENT_DOTS,
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const button = wrapper.findComponent(DsButton);
|
|
178
|
+
expect(button.exists()).toBe(true);
|
|
179
|
+
expect(button.text()).toBe('Confirm');
|
|
180
|
+
expect(button.props('iconLeft')).toStrictEqual(ICONS.FA_COMMENT_DOTS);
|
|
181
|
+
expect(button.props('color')).toBe(BUTTON_COLORS.PRIMARY);
|
|
182
|
+
expect(button.props('type')).toBe(BUTTON_TYPES.FILLED);
|
|
183
|
+
expect(button.props('size')).toBe(BUTTON_SIZES.SMALL);
|
|
184
|
+
expect(button.props('radius')).toBe(BUTTON_RADIUSES.ROUNDED);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it.each(Object.values(CALLOUT_ICON_COLORS))(
|
|
188
|
+
'keeps the filled primary button for icon color "%s"',
|
|
189
|
+
(iconColor) => {
|
|
190
|
+
const wrapper = mountCallout({ props: { buttonLabel: 'Confirm', iconColor } });
|
|
191
|
+
|
|
192
|
+
const button = wrapper.findComponent(DsButton);
|
|
193
|
+
expect(button.props('color')).toBe(BUTTON_COLORS.PRIMARY);
|
|
194
|
+
expect(button.props('type')).toBe(BUTTON_TYPES.FILLED);
|
|
195
|
+
},
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
it('emits button-clicked when the default button is clicked', async () => {
|
|
199
|
+
const wrapper = mountCallout({
|
|
200
|
+
props: {
|
|
201
|
+
buttonLabel: 'Confirm',
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
await wrapper.findComponent(DsButton).trigger('click');
|
|
206
|
+
|
|
207
|
+
expect(wrapper.emitted('button-clicked')).toHaveLength(1);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('renders the actions slot instead of the default button', () => {
|
|
211
|
+
const wrapper = mountCallout({
|
|
212
|
+
props: {
|
|
213
|
+
buttonLabel: 'Default button',
|
|
214
|
+
},
|
|
215
|
+
slots: {
|
|
216
|
+
actions: () => h('span', { class: 'custom-action' }, 'Custom action'),
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
expect(wrapper.find('.custom-action').exists()).toBe(true);
|
|
221
|
+
expect(wrapper.findComponent(DsButton).exists()).toBe(false);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('does not render the actions block when there is no button label and no slot', () => {
|
|
225
|
+
const wrapper = mountCallout();
|
|
226
|
+
|
|
227
|
+
expect(wrapper.find('.ds-callout__actions').exists()).toBe(false);
|
|
228
|
+
expect(wrapper.findComponent(DsButton).exists()).toBe(false);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('renders the actions block when only the slot is provided', () => {
|
|
232
|
+
const wrapper = mountCallout({
|
|
233
|
+
slots: {
|
|
234
|
+
actions: () => h('span', { class: 'custom-action' }, 'Custom action'),
|
|
235
|
+
},
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
expect(wrapper.find('.ds-callout__actions').exists()).toBe(true);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('adds the stacked actions modifier on the root when isActionVertical is set', () => {
|
|
242
|
+
const wrapper = mountCallout({
|
|
243
|
+
props: {
|
|
244
|
+
isActionVertical: true,
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
expect(wrapper.find('.ds-callout').classes()).toContain('-ds-actions-vertical');
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('does not add the stacked actions modifier by default', () => {
|
|
252
|
+
const wrapper = mountCallout();
|
|
253
|
+
|
|
254
|
+
expect(wrapper.find('.ds-callout').classes()).not.toContain('-ds-actions-vertical');
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
});
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { ComponentProps } from 'vue-component-type-helpers';
|
|
2
|
+
import { Meta, StoryObj } from '@storybook/vue3';
|
|
3
|
+
import { action } from '@storybook/addon-actions';
|
|
4
|
+
|
|
5
|
+
import Callout from './Callout.vue';
|
|
6
|
+
import {
|
|
7
|
+
CALLOUT_ICON_COLORS,
|
|
8
|
+
CALLOUT_LAYOUTS,
|
|
9
|
+
CALLOUT_MAIN_TEXT_COLORS,
|
|
10
|
+
CALLOUT_SIZES,
|
|
11
|
+
} from './Callout.consts';
|
|
12
|
+
import DsButton from '../Buttons/Button';
|
|
13
|
+
import { BUTTON_COLORS, BUTTON_SIZES, BUTTON_TYPES } from '../Buttons/Button/Button.consts';
|
|
14
|
+
import { ICONS } from '../Icons/Icon';
|
|
15
|
+
|
|
16
|
+
type CalloutProps = ComponentProps<typeof Callout>;
|
|
17
|
+
|
|
18
|
+
const VERTICAL_FIGMA_URL =
|
|
19
|
+
'https://www.figma.com/design/izQdYyiBR1GQgFkaOIfIJI/LMS---DS-Components?node-id=17359-1009&m=dev';
|
|
20
|
+
const HORIZONTAL_FIGMA_URL =
|
|
21
|
+
'https://www.figma.com/design/izQdYyiBR1GQgFkaOIfIJI/LMS---DS-Components?node-id=17760-416&m=dev';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Clearing a text control yields `''`, which TextGroup renders as a ` ` placeholder row.
|
|
25
|
+
* Mapping it to `null` removes the row instead, which is what an empty control should mean.
|
|
26
|
+
*/
|
|
27
|
+
const emptyToNull = (value?: string | null) => value || null;
|
|
28
|
+
|
|
29
|
+
const mapArgs = (args: CalloutProps) => ({
|
|
30
|
+
...args,
|
|
31
|
+
icon: ICONS[args.icon],
|
|
32
|
+
buttonIcon: ICONS[args.buttonIcon],
|
|
33
|
+
eyebrowText: emptyToNull(args.eyebrowText),
|
|
34
|
+
mainText: emptyToNull(args.mainText),
|
|
35
|
+
supportingText: emptyToNull(args.supportingText),
|
|
36
|
+
buttonLabel: emptyToNull(args.buttonLabel),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const meta: Meta<typeof Callout> = {
|
|
40
|
+
title: 'Components/Callout',
|
|
41
|
+
component: Callout,
|
|
42
|
+
render: (args: CalloutProps) => ({
|
|
43
|
+
components: { Callout },
|
|
44
|
+
setup() {
|
|
45
|
+
return { onButtonClicked: action('button-clicked') };
|
|
46
|
+
},
|
|
47
|
+
computed: {
|
|
48
|
+
props() {
|
|
49
|
+
return mapArgs(args);
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
template: `<div style="max-width: 500px;"><Callout v-bind="props" @button-clicked="onButtonClicked" /></div>`,
|
|
53
|
+
}),
|
|
54
|
+
argTypes: {
|
|
55
|
+
layout: {
|
|
56
|
+
control: 'select',
|
|
57
|
+
options: Object.values(CALLOUT_LAYOUTS),
|
|
58
|
+
},
|
|
59
|
+
size: {
|
|
60
|
+
control: 'select',
|
|
61
|
+
options: Object.values(CALLOUT_SIZES),
|
|
62
|
+
},
|
|
63
|
+
iconColor: {
|
|
64
|
+
control: 'select',
|
|
65
|
+
options: Object.values(CALLOUT_ICON_COLORS),
|
|
66
|
+
},
|
|
67
|
+
mainTextColor: {
|
|
68
|
+
control: 'select',
|
|
69
|
+
options: Object.values(CALLOUT_MAIN_TEXT_COLORS),
|
|
70
|
+
},
|
|
71
|
+
icon: {
|
|
72
|
+
control: 'select',
|
|
73
|
+
options: Object.keys(ICONS),
|
|
74
|
+
},
|
|
75
|
+
buttonIcon: {
|
|
76
|
+
control: 'select',
|
|
77
|
+
options: [null, ...Object.keys(ICONS)],
|
|
78
|
+
},
|
|
79
|
+
eyebrowText: { control: 'text' },
|
|
80
|
+
mainText: { control: 'text' },
|
|
81
|
+
supportingText: { control: 'text' },
|
|
82
|
+
buttonLabel: { control: 'text' },
|
|
83
|
+
isActionVertical: { control: 'boolean' },
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
export default meta;
|
|
87
|
+
|
|
88
|
+
type Story = StoryObj<typeof Callout>;
|
|
89
|
+
|
|
90
|
+
export const Interactive: Story = {
|
|
91
|
+
args: {
|
|
92
|
+
layout: CALLOUT_LAYOUTS.VERTICAL,
|
|
93
|
+
size: CALLOUT_SIZES.MEDIUM,
|
|
94
|
+
iconColor: CALLOUT_ICON_COLORS.PRIMARY,
|
|
95
|
+
mainTextColor: CALLOUT_MAIN_TEXT_COLORS.PRIMARY,
|
|
96
|
+
icon: 'FA_COMMENT_DOTS' as unknown as CalloutProps['icon'],
|
|
97
|
+
eyebrowText: '',
|
|
98
|
+
mainText: 'Main text tutaj sobie będzie',
|
|
99
|
+
supportingText: 'Supporting text tutaj będzie',
|
|
100
|
+
buttonLabel: 'Button label',
|
|
101
|
+
buttonIcon: null,
|
|
102
|
+
isActionVertical: false,
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
Interactive.parameters = {
|
|
107
|
+
design: {
|
|
108
|
+
type: 'figma',
|
|
109
|
+
url: VERTICAL_FIGMA_URL,
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const WithActionsSlot: Story = {
|
|
114
|
+
render: (args: CalloutProps) => ({
|
|
115
|
+
components: { Callout, DsButton },
|
|
116
|
+
setup() {
|
|
117
|
+
return { args, ICONS, BUTTON_COLORS, BUTTON_SIZES, BUTTON_TYPES };
|
|
118
|
+
},
|
|
119
|
+
computed: {
|
|
120
|
+
props() {
|
|
121
|
+
return mapArgs(args);
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
template: `
|
|
125
|
+
<div style="max-width: 500px;">
|
|
126
|
+
<Callout v-bind="props">
|
|
127
|
+
<template #actions>
|
|
128
|
+
<ds-button :color="BUTTON_COLORS.NEUTRAL" :type="BUTTON_TYPES.OUTLINED" :size="BUTTON_SIZES.SMALL">Cancel</ds-button>
|
|
129
|
+
<ds-button :color="BUTTON_COLORS.PRIMARY" :type="BUTTON_TYPES.FILLED" :size="BUTTON_SIZES.SMALL">Confirm</ds-button>
|
|
130
|
+
</template>
|
|
131
|
+
</Callout>
|
|
132
|
+
</div>`,
|
|
133
|
+
}),
|
|
134
|
+
args: {
|
|
135
|
+
layout: CALLOUT_LAYOUTS.VERTICAL,
|
|
136
|
+
size: CALLOUT_SIZES.MEDIUM,
|
|
137
|
+
iconColor: CALLOUT_ICON_COLORS.PRIMARY,
|
|
138
|
+
mainTextColor: CALLOUT_MAIN_TEXT_COLORS.PRIMARY,
|
|
139
|
+
icon: 'FA_COMMENT_DOTS' as unknown as CalloutProps['icon'],
|
|
140
|
+
mainText: 'Main text tutaj sobie będzie',
|
|
141
|
+
supportingText: 'Supporting text tutaj będzie',
|
|
142
|
+
isActionVertical: false,
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
WithActionsSlot.parameters = {
|
|
147
|
+
design: {
|
|
148
|
+
type: 'figma',
|
|
149
|
+
url: VERTICAL_FIGMA_URL,
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* The full variant matrix, laid out for side-by-side comparison with the Figma component set
|
|
155
|
+
* (node 17696:2260).
|
|
156
|
+
*/
|
|
157
|
+
export const Variants: Story = {
|
|
158
|
+
render: () => ({
|
|
159
|
+
components: { Callout },
|
|
160
|
+
setup() {
|
|
161
|
+
return {
|
|
162
|
+
ICONS,
|
|
163
|
+
CALLOUT_LAYOUTS,
|
|
164
|
+
CALLOUT_SIZES,
|
|
165
|
+
sizes: Object.values(CALLOUT_SIZES),
|
|
166
|
+
};
|
|
167
|
+
},
|
|
168
|
+
template: `
|
|
169
|
+
<div style="display: flex; flex-direction: column; gap: 48px;">
|
|
170
|
+
<section v-for="layout in [CALLOUT_LAYOUTS.VERTICAL, CALLOUT_LAYOUTS.HORIZONTAL]" :key="layout">
|
|
171
|
+
<h3 style="margin-bottom: 16px;">{{ layout }}</h3>
|
|
172
|
+
<div style="display: flex; flex-direction: column; gap: 32px; max-width: 500px;">
|
|
173
|
+
<Callout
|
|
174
|
+
v-for="size in sizes"
|
|
175
|
+
:key="size"
|
|
176
|
+
:layout="layout"
|
|
177
|
+
:size="size"
|
|
178
|
+
:icon="ICONS.FA_COMMENT_DOTS"
|
|
179
|
+
:eyebrow-text="size === CALLOUT_SIZES.SMALL ? 'Eyebrow text' : null"
|
|
180
|
+
:main-text="'Main text tutaj sobie będzie (' + size + ')'"
|
|
181
|
+
supporting-text="Supporting text tutaj będzie"
|
|
182
|
+
button-label="Button small"
|
|
183
|
+
/>
|
|
184
|
+
<Callout
|
|
185
|
+
:layout="layout"
|
|
186
|
+
:icon="ICONS.FA_COMMENT_DOTS"
|
|
187
|
+
main-text="Main text tutaj sobie będzie (stacked actions)"
|
|
188
|
+
supporting-text="Supporting text tutaj będzie"
|
|
189
|
+
button-label="Button small"
|
|
190
|
+
is-action-vertical
|
|
191
|
+
/>
|
|
192
|
+
<Callout
|
|
193
|
+
:layout="layout"
|
|
194
|
+
:icon="ICONS.FA_COMMENT_DOTS"
|
|
195
|
+
main-text="Main text tutaj sobie będzie (no actions)"
|
|
196
|
+
supporting-text="Supporting text tutaj będzie"
|
|
197
|
+
/>
|
|
198
|
+
</div>
|
|
199
|
+
</section>
|
|
200
|
+
</div>`,
|
|
201
|
+
}),
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
Variants.parameters = {
|
|
205
|
+
design: {
|
|
206
|
+
type: 'figma',
|
|
207
|
+
url: HORIZONTAL_FIGMA_URL,
|
|
208
|
+
},
|
|
209
|
+
};
|