@bethinkpl/design-system 43.0.0 → 44.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.
Files changed (51) hide show
  1. package/dist/design-system.css +1 -1
  2. package/dist/design-system.js +11664 -11121
  3. package/dist/design-system.js.map +1 -1
  4. package/dist/lib/js/components/Form/PinInputField/PinInputField.consts.d.ts +14 -0
  5. package/dist/lib/js/components/Form/PinInputField/PinInputField.types.d.ts +12 -0
  6. package/dist/lib/js/components/Form/PinInputField/PinInputField.utils.d.ts +2 -0
  7. package/dist/lib/js/components/Form/PinInputField/PinInputField.vue.d.ts +91 -0
  8. package/dist/lib/js/components/Form/PinInputField/index.d.ts +5 -0
  9. package/dist/lib/js/components/Form/PinInputField/usePinInputFieldWithinForm.d.ts +8 -0
  10. package/dist/lib/js/components/Form/TextAreaField/TextAreaField.types.d.ts +9 -0
  11. package/dist/lib/js/components/Form/TextAreaField/TextAreaField.vue.d.ts +76 -0
  12. package/dist/lib/js/components/Form/TextAreaField/TextAreaFieldAutosized.vue.d.ts +6 -0
  13. package/dist/lib/js/components/Form/TextAreaField/index.d.ts +4 -0
  14. package/dist/lib/js/components/LabelValue/LabelValueItem/LabelValueItem.vue.d.ts +12 -1
  15. package/dist/lib/js/components/Toast/Toast.consts.d.ts +11 -9
  16. package/dist/lib/js/components/Toast/Toast.vue.d.ts +51 -433
  17. package/dist/lib/js/{components/Form/InputField/useInputFieldWithinForm.d.ts → composables/useTextFieldWithinForm.d.ts} +1 -1
  18. package/dist/lib/js/index.d.ts +4 -0
  19. package/lib/js/components/Form/CheckboxGroupField/CheckboxGroupField.spec.ts +15 -0
  20. package/lib/js/components/Form/CheckboxGroupField/CheckboxGroupField.vue +5 -2
  21. package/lib/js/components/Form/Form.stories.ts +23 -2
  22. package/lib/js/components/Form/InputField/InputField.spec.ts +11 -1
  23. package/lib/js/components/Form/InputField/InputField.vue +7 -4
  24. package/lib/js/components/Form/PinInputField/PinInputField.consts.ts +18 -0
  25. package/lib/js/components/Form/PinInputField/PinInputField.spec.ts +394 -0
  26. package/lib/js/components/Form/PinInputField/PinInputField.stories.ts +124 -0
  27. package/lib/js/components/Form/PinInputField/PinInputField.types.ts +42 -0
  28. package/lib/js/components/Form/PinInputField/PinInputField.utils.ts +5 -0
  29. package/lib/js/components/Form/PinInputField/PinInputField.vue +211 -0
  30. package/lib/js/components/Form/PinInputField/index.ts +6 -0
  31. package/lib/js/components/Form/PinInputField/usePinInputFieldWithinForm.ts +29 -0
  32. package/lib/js/components/Form/SelectField/SelectField.stories.ts +0 -5
  33. package/lib/js/components/Form/SelectField/useSelectFieldWithinForm.ts +1 -1
  34. package/lib/js/components/Form/TextAreaField/TextAreaField.spec.ts +341 -0
  35. package/lib/js/components/Form/TextAreaField/TextAreaField.stories.ts +99 -0
  36. package/lib/js/components/Form/TextAreaField/TextAreaField.types.ts +23 -0
  37. package/lib/js/components/Form/TextAreaField/TextAreaField.vue +190 -0
  38. package/lib/js/components/Form/TextAreaField/TextAreaFieldAutosized.vue +19 -0
  39. package/lib/js/components/Form/TextAreaField/index.ts +5 -0
  40. package/lib/js/components/LabelValue/LabelValueItem/LabelValueItem.spec.ts +26 -0
  41. package/lib/js/components/LabelValue/LabelValueItem/LabelValueItem.stories.ts +16 -3
  42. package/lib/js/components/LabelValue/LabelValueItem/LabelValueItem.vue +5 -0
  43. package/lib/js/components/LoadingBar/LoadingBar.spec.ts +84 -0
  44. package/lib/js/components/LoadingBar/LoadingBar.vue +30 -19
  45. package/lib/js/components/Toast/Toast.consts.ts +4 -2
  46. package/lib/js/components/Toast/Toast.spec.ts +177 -0
  47. package/lib/js/components/Toast/Toast.stories.ts +65 -31
  48. package/lib/js/components/Toast/Toast.vue +176 -195
  49. package/lib/js/{components/Form/InputField/useInputFieldWithinForm.ts → composables/useTextFieldWithinForm.ts} +7 -2
  50. package/lib/js/index.ts +4 -0
  51. package/package.json +1 -1
@@ -0,0 +1,177 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest';
2
+ import { mount } from '@vue/test-utils';
3
+ import { h } from 'vue';
4
+ import Toast from './Toast.vue';
5
+ import { TOAST_COLORS, TOAST_POSITIONS, TOAST_SIZES } from './Toast.consts';
6
+ import DsButton, { BUTTON_SIZES } from '../Buttons/Button';
7
+ import DsIconButton, { ICON_BUTTON_COLORS, ICON_BUTTON_SIZES } from '../Buttons/IconButton';
8
+ import { ICONS } from '../Icons/Icon';
9
+ import { ComponentProps } from 'vue-component-type-helpers';
10
+
11
+ const setup = (props: ComponentProps<typeof Toast> = {}) =>
12
+ mount(Toast, {
13
+ props,
14
+ slots: {
15
+ content: () => h('span', 'Wpłynąłem na suchego przestwór oceanu'),
16
+ },
17
+ });
18
+
19
+ describe('Toast', () => {
20
+ afterEach(() => {
21
+ vi.useRealTimers();
22
+ });
23
+
24
+ it('should render the content slot', () => {
25
+ const wrapper = setup();
26
+
27
+ expect(wrapper.find('.ds-toast__content').text()).toBe(
28
+ 'Wpłynąłem na suchego przestwór oceanu',
29
+ );
30
+ });
31
+
32
+ it.each(Object.values(TOAST_POSITIONS))('should render in position: %s', (position) => {
33
+ const wrapper = setup({ position });
34
+
35
+ expect(wrapper.find('.ds-toast').classes()).toContain(`-ds-position-${position}`);
36
+ });
37
+
38
+ it('should update the position modifier when the prop changes', async () => {
39
+ const wrapper = setup({ position: TOAST_POSITIONS.CENTER });
40
+
41
+ await wrapper.setProps({ position: TOAST_POSITIONS.NONE });
42
+
43
+ expect(wrapper.find('.ds-toast').classes()).toContain('-ds-position-none');
44
+ expect(wrapper.find('.ds-toast').classes()).not.toContain('-ds-position-center');
45
+ });
46
+
47
+ it.each(Object.values(TOAST_SIZES))('should render in size: %s', (size) => {
48
+ const wrapper = setup({ size });
49
+
50
+ expect(wrapper.find('.ds-toast').classes()).toContain(`-ds-size-${size}`);
51
+ });
52
+
53
+ it.each(Object.values(TOAST_COLORS))('should render in color: %s', (color) => {
54
+ const wrapper = setup({ color });
55
+
56
+ expect(wrapper.find('.ds-toast').classes()).toContain(`-ds-color-${color}`);
57
+ });
58
+
59
+ it('should render the title when provided', () => {
60
+ const wrapper = setup({ title: 'Tytuł' });
61
+
62
+ expect(wrapper.find('.ds-toast__title').text()).toBe('Tytuł');
63
+ });
64
+
65
+ it('should not render the title when not provided', () => {
66
+ const wrapper = setup();
67
+
68
+ expect(wrapper.find('.ds-toast__title').exists()).toBe(false);
69
+ });
70
+
71
+ it.each([
72
+ { size: TOAST_SIZES.SMALL, expectedButtonSize: BUTTON_SIZES.SMALL },
73
+ { size: TOAST_SIZES.MEDIUM, expectedButtonSize: BUTTON_SIZES.MEDIUM },
74
+ { size: TOAST_SIZES.LARGE, expectedButtonSize: BUTTON_SIZES.MEDIUM },
75
+ ])(
76
+ 'should render $expectedButtonSize footer buttons in size $size',
77
+ ({ size, expectedButtonSize }) => {
78
+ const wrapper = setup({
79
+ size,
80
+ footerPrimaryButtonText: 'primary',
81
+ footerSecondaryButtonText: 'secondary',
82
+ });
83
+
84
+ const buttonSizes = wrapper
85
+ .findAllComponents(DsButton)
86
+ .map((button) => button.props('size'));
87
+
88
+ expect(buttonSizes).toEqual([expectedButtonSize, expectedButtonSize]);
89
+ },
90
+ );
91
+
92
+ it('should not render the footer without button texts', () => {
93
+ const wrapper = setup();
94
+
95
+ expect(wrapper.find('.ds-toast__footerButtons').exists()).toBe(false);
96
+ });
97
+
98
+ it('should emit primary-button-click on the primary button click', async () => {
99
+ const wrapper = setup({ footerPrimaryButtonText: 'primary' });
100
+
101
+ await wrapper.findComponent(DsButton).trigger('click');
102
+
103
+ expect(wrapper.emitted('primary-button-click')).toHaveLength(1);
104
+ });
105
+
106
+ it('should emit secondary-button-click on the secondary button click', async () => {
107
+ const wrapper = setup({ footerSecondaryButtonText: 'secondary' });
108
+
109
+ await wrapper.findComponent(DsButton).trigger('click');
110
+
111
+ expect(wrapper.emitted('secondary-button-click')).toHaveLength(1);
112
+ });
113
+
114
+ it('should emit close after the disappearing timeout', () => {
115
+ vi.useFakeTimers();
116
+ const wrapper = setup({ isDisappearing: true, disappearingTimeout: '3' });
117
+
118
+ vi.advanceTimersByTime(3 * 1000 + 100);
119
+
120
+ expect(wrapper.emitted('close')).toHaveLength(1);
121
+ });
122
+
123
+ it('should not emit close when it is not disappearing', () => {
124
+ vi.useFakeTimers();
125
+ const wrapper = setup({ isDisappearing: false, disappearingTimeout: '3' });
126
+
127
+ vi.advanceTimersByTime(3 * 1000 + 100);
128
+
129
+ expect(wrapper.emitted('close')).toBeUndefined();
130
+ });
131
+
132
+ it('should not render the close button by default', () => {
133
+ const wrapper = setup();
134
+
135
+ expect(wrapper.find('.ds-toast__close').exists()).toBe(false);
136
+ expect(wrapper.find('.ds-toast').classes()).not.toContain('-ds-closable');
137
+ });
138
+
139
+ it('should render the close button when it is closable', () => {
140
+ const wrapper = setup({ isClosable: true });
141
+
142
+ expect(wrapper.find('.ds-toast__close').exists()).toBe(true);
143
+ expect(wrapper.find('.ds-toast').classes()).toContain('-ds-closable');
144
+ });
145
+
146
+ it('should render the close button as a small neutral-weak xmark', () => {
147
+ const wrapper = setup({ isClosable: true });
148
+
149
+ const closeButton = wrapper.findComponent(DsIconButton);
150
+
151
+ expect(closeButton.props('icon')).toBe(ICONS.FA_XMARK);
152
+ expect(closeButton.props('size')).toBe(ICON_BUTTON_SIZES.SMALL);
153
+ expect(closeButton.props('color')).toBe(ICON_BUTTON_COLORS.NEUTRAL_WEAK);
154
+ });
155
+
156
+ it('should emit close on the close button click', async () => {
157
+ const wrapper = setup({ isClosable: true });
158
+
159
+ await wrapper.find('.ds-toast__close > *').trigger('click');
160
+
161
+ expect(wrapper.emitted('close')).toHaveLength(1);
162
+ });
163
+
164
+ it('should emit close only once when the close button pre-empts the disappearing timeout', async () => {
165
+ vi.useFakeTimers();
166
+ const wrapper = setup({
167
+ isClosable: true,
168
+ isDisappearing: true,
169
+ disappearingTimeout: '3',
170
+ });
171
+
172
+ await wrapper.find('.ds-toast__close > *').trigger('click');
173
+ vi.advanceTimersByTime(3 * 1000 + 100);
174
+
175
+ expect(wrapper.emitted('close')).toHaveLength(1);
176
+ });
177
+ });
@@ -10,6 +10,26 @@ export default {
10
10
  decorators: [withActions],
11
11
  } as Meta<typeof DsToast>;
12
12
 
13
+ const toastTemplate = `<ds-toast
14
+ v-if="isVisible"
15
+ :title="title"
16
+ :size="size"
17
+ :position="position"
18
+ :color="color"
19
+ :footer-primary-button-text="footerPrimaryButtonText"
20
+ :footer-primary-button-icon="ICONS[footerPrimaryButtonIcon]"
21
+ :footer-secondary-button-text="footerSecondaryButtonText"
22
+ :footer-secondary-button-icon="ICONS[footerSecondaryButtonIcon]"
23
+ :is-disappearing="isDisappearing"
24
+ :disappearing-timeout="disappearingTimeout"
25
+ :is-closable="isClosable"
26
+ @close="isVisible = false"
27
+ >
28
+ <template #content>
29
+ <span v-html="content" />
30
+ </template>
31
+ </ds-toast>`;
32
+
13
33
  const StoryTemplate: StoryFn<typeof DsToast> = (args) => ({
14
34
  components: { DsToast },
15
35
  setup() {
@@ -19,40 +39,41 @@ const StoryTemplate: StoryFn<typeof DsToast> = (args) => ({
19
39
  return {
20
40
  ICONS: Object.freeze(ICONS),
21
41
  isVisible: true,
22
- boundariesSelectorId:
23
- this.boundariesSelector != null ? `#${this.boundariesSelector}` : null,
24
42
  };
25
43
  },
26
- template: `<div style="display: flex; justify-content: space-around; height: 1200px; width: 100%;">
27
- <ds-toast
28
- v-if="isVisible"
29
- :size="size"
30
- :position="position"
31
- :boundaries-selector="boundariesSelectorId"
32
- :color="color"
33
- :footer-primary-button-text="footerPrimaryButtonText"
34
- :footer-primary-button-icon="ICONS[footerPrimaryButtonIcon]"
35
- :footer-secondary-button-text="footerSecondaryButtonText"
36
- :footer-secondary-button-icon="ICONS[footerSecondaryButtonIcon]"
37
- :is-disappearing="isDisappearing"
38
- :disappearing-timeout="disappearingTimeout"
39
- @close="isVisible = false"
40
- >
41
- <template #content>
42
- <span v-html="content" />
43
- </template>
44
- </ds-toast>
45
- <div id="left" style="width: 25%; height: 100%; border: 1px black solid;"></div>
46
- <div id="right" style="width: 25%; height: 100%; border: 1px black solid;"></div>
44
+ template: `<div style="height: 1200px; width: 100%;">
45
+ ${toastTemplate}
46
+ </div>`,
47
+ });
48
+
49
+ // `position: none` leaves the placement to the consumer — here the wrapper anchors the toast to the
50
+ // bottom right of its own container instead of the viewport.
51
+ const ClientPositionedStoryTemplate: StoryFn<typeof DsToast> = (args) => ({
52
+ components: { DsToast },
53
+ setup() {
54
+ return args;
55
+ },
56
+ data() {
57
+ return {
58
+ ICONS: Object.freeze(ICONS),
59
+ isVisible: true,
60
+ };
61
+ },
62
+ template: `<div style="height: 400px; padding: 24px; width: 100%;">
63
+ <div style="position: relative; height: 100%; border: 1px black solid;">
64
+ <div style="position: absolute; bottom: 16px; right: 16px;">
65
+ ${toastTemplate}
66
+ </div>
67
+ </div>
47
68
  </div>`,
48
69
  });
49
70
 
50
71
  export const Interactive = StoryTemplate.bind({});
51
72
 
52
73
  const args = {
74
+ title: 'Opcjonalny Title wpisz tutaj',
53
75
  size: TOAST_SIZES.MEDIUM,
54
76
  position: TOAST_POSITIONS.CENTER,
55
- boundariesSelector: null,
56
77
  color: TOAST_COLORS.INFO,
57
78
  footerPrimaryButtonText: 'primary',
58
79
  footerPrimaryButtonIcon: null,
@@ -60,10 +81,14 @@ const args = {
60
81
  footerSecondaryButtonIcon: null,
61
82
  isDisappearing: false,
62
83
  disappearingTimeout: '0',
84
+ isClosable: true,
63
85
  content: 'Wpłynąłem na suchego przestwór oceanu',
64
86
  } as Args;
65
87
 
66
88
  const argTypes = {
89
+ title: {
90
+ control: 'text',
91
+ },
67
92
  size: {
68
93
  control: 'select',
69
94
  options: Object.values(TOAST_SIZES),
@@ -72,10 +97,6 @@ const argTypes = {
72
97
  control: 'select',
73
98
  options: Object.values(TOAST_POSITIONS),
74
99
  },
75
- boundariesSelector: {
76
- control: 'select',
77
- options: [null, 'left', 'right'],
78
- },
79
100
  color: {
80
101
  control: 'select',
81
102
  options: Object.values(TOAST_COLORS),
@@ -88,20 +109,33 @@ const argTypes = {
88
109
  control: 'select',
89
110
  options: [null, ...Object.keys(ICONS)],
90
111
  },
112
+ isClosable: {
113
+ control: 'boolean',
114
+ },
91
115
  content: {
92
116
  control: 'text',
93
117
  },
94
118
  } as ArgTypes;
95
119
 
120
+ const actionsParameters = {
121
+ actions: {
122
+ handles: ['close', 'primary-button-click', 'secondary-button-click'],
123
+ },
124
+ };
125
+
96
126
  Interactive.argTypes = argTypes;
97
127
  Interactive.args = args;
98
128
 
99
129
  Interactive.parameters = {
100
- actions: {
101
- handles: ['close', 'primary-button-click', 'secondary-button-click'],
102
- },
130
+ ...actionsParameters,
103
131
  design: {
104
132
  type: 'figma',
105
133
  url: 'https://www.figma.com/design/izQdYyiBR1GQgFkaOIfIJI/LMS---DS-Components?node-id=8091-108960',
106
134
  },
107
135
  };
136
+
137
+ export const ClientPositioned = ClientPositionedStoryTemplate.bind({});
138
+
139
+ ClientPositioned.argTypes = argTypes;
140
+ ClientPositioned.args = { ...args, position: TOAST_POSITIONS.NONE };
141
+ ClientPositioned.parameters = actionsParameters;