@eturnity/eturnity_reusable_components 9.22.2 → 9.25.1
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/index.es3.js +268 -156
- package/package.json +3 -3
- package/src/assets/svgIcons/add_thin_icon.svg +3 -0
- package/src/assets/svgIcons/delete.svg +2 -3
- package/src/assets/svgIcons/location_outline.svg +3 -0
- package/src/assets/svgIcons/redo.svg +2 -5
- package/src/assets/svgIcons/save.svg +2 -2
- package/src/assets/svgIcons/subtract_thin_icon.svg +3 -0
- package/src/assets/svgIcons/undo.svg +2 -5
- package/src/assets/theme.js +202 -90
- package/src/components/accordion/Accordion.stories.js +1 -1
- package/src/components/addNewButton/AddNewButton.stories.js +40 -8
- package/src/components/banner/actionBanner/ActionBanner.stories.js +1 -1
- package/src/components/banner/actionBanner/index.vue +4 -2
- package/src/components/banner/banner/Banner.stories.js +1 -1
- package/src/components/banner/infoBanner/InfoBanner.stories.js +1 -1
- package/src/components/banner/infoBanner/index.vue +4 -2
- package/src/components/banner/notificationBanner/notificationBanner.stories.js +81 -0
- package/src/components/buttons/buttonIcon/index.vue +213 -81
- package/src/components/buttons/closeButton/CloseButton.stories.js +1 -1
- package/src/components/buttons/collection/index.vue +1 -3
- package/src/components/buttons/mainButton/index.vue +99 -45
- package/src/components/buttons/splitButtons/splitButtons.stories.js +70 -0
- package/src/components/card/Card.stories.js +16 -4
- package/src/components/card/index.vue +1 -1
- package/src/components/collapsableInfoText/collapsableInfoText.stories.js +52 -0
- package/src/components/deleteIcon/DeleteIcon.stories.js +1 -1
- package/src/components/draggableCard/draggableCard.stories.js +1 -1
- package/src/components/draggableInputHandle/draggableInputHandle.stories.js +43 -0
- package/src/components/dropdown/Dropdown.stories.js +1 -1
- package/src/components/errorMessage/errorMessage.stories.js +31 -17
- package/src/components/filter/filterSettings.vue +1 -1
- package/src/components/icon/Icon.stories.js +1 -1
- package/src/components/iconWrapper/iconWrapper.stories.js +78 -0
- package/src/components/infoCard/InfoCard.stories.js +1 -1
- package/src/components/infoLabel/infoLabel.stories.js +61 -0
- package/src/components/infoText/infoText.stories.js +1 -1
- package/src/components/inputs/checkbox/Checkbox.stories.js +1 -1
- package/src/components/inputs/checkbox/index.vue +10 -1
- package/src/components/inputs/colorSelector/ColorSelector.stories.js +78 -0
- package/src/components/inputs/colorSelector/colorSelector.spec.js +73 -0
- package/src/components/inputs/colorSelector/defaultProps.js +11 -0
- package/src/components/inputs/colorSelector/index.vue +224 -0
- package/src/components/inputs/inputNumber/InputNumber.stories.js +1 -1
- package/src/components/inputs/inputNumber/index.vue +69 -59
- package/src/components/inputs/inputNumberQuestion/inputNumberQuestion.stories.js +77 -0
- package/src/components/inputs/inputText/InputText.stories.js +1 -1
- package/src/components/inputs/inputText/index.vue +29 -20
- package/src/components/inputs/isRequiredLabelStar/isRequiredLabelStar.stories.js +26 -0
- package/src/components/inputs/radioButton/RadioButton.stories.js +1 -1
- package/src/components/inputs/radioButton/index.vue +21 -7
- package/src/components/inputs/searchInput/SearchInput.stories.js +1 -1
- package/src/components/inputs/select/index.vue +33 -21
- package/src/components/inputs/select/option/index.vue +1 -1
- package/src/components/inputs/select/select.stories.js +4 -25
- package/src/components/inputs/shared/inputLabelTypography.js +7 -0
- package/src/components/inputs/slider/index.vue +9 -12
- package/src/components/inputs/slider/slider.stories.js +71 -0
- package/src/components/inputs/switchField/index.vue +38 -10
- package/src/components/inputs/switchField/switchField.stories.js +72 -0
- package/src/components/inputs/textAreaInput/TextAreaInput.stories.js +1 -1
- package/src/components/inputs/textAreaInput/index.vue +19 -6
- package/src/components/inputs/toggle/Toggle.stories.js +1 -1
- package/src/components/inputs/toggle/index.vue +10 -6
- package/src/components/label/index.vue +39 -11
- package/src/components/label/label.stories.js +69 -0
- package/src/components/markerItem/markerItem.stories.js +46 -0
- package/src/components/modals/actionModal/actionModal.stories.js +1 -1
- package/src/components/modals/actionModal/index.vue +1 -1
- package/src/components/modals/infoModal/index.vue +1 -10
- package/src/components/modals/infoModal/infoModal.stories.js +1 -1
- package/src/components/modals/modal/modal.spec.js +168 -0
- package/src/components/modals/modal/modal.stories.js +287 -26
- package/src/components/modals/modalBody/index.vue +30 -0
- package/src/components/modals/modalButtonContainer/index.vue +42 -0
- package/src/components/modals/modalButtonContainer/modalButtonContainer.stories.js +59 -0
- package/src/components/modals/modalContent/index.vue +125 -0
- package/src/components/modals/modalTitle/index.vue +34 -0
- package/src/components/navigationTabs/navigationTabs.stories.js +58 -0
- package/src/components/pageSubtitle/PageSubtitle.stories.js +1 -1
- package/src/components/pageTitle/PageTitle.stories.js +1 -1
- package/src/components/pagination/pagination.stories.js +79 -0
- package/src/components/paginationV2/paginationV2.spec.js +132 -0
- package/src/components/paginationV2/paginationV2.stories.js +68 -0
- package/src/components/panelRangeInfo/panelRangeInfo.stories.js +60 -0
- package/src/components/progressBar/ProgressBar.stories.js +1 -1
- package/src/components/progressStep/progressStep.stories.js +1 -1
- package/src/components/projectMarker/ProjectMarker.stories.js +1 -1
- package/src/components/rangeSlider/RangeSlider.stories.js +1 -1
- package/src/components/roundTabs/roundTabs.stories.js +54 -0
- package/src/components/selectedOptions/selectedOptions.stories.js +1 -1
- package/src/components/sideMenu/sideMenu.stories.js +53 -0
- package/src/components/spinner/Spinner.stories.js +1 -1
- package/src/components/spinnerGif/SpinnerGif.stories.js +1 -1
- package/src/components/statusIndicator/statusIndicator.stories.js +101 -0
- package/src/components/tableDropdown/TableDropdown.stories.js +1 -1
- package/src/components/tables/viewTable/viewTable.stories.js +85 -0
- package/src/components/tabsHeader/TabsHeader.stories.js +1 -1
- package/src/components/tag/conversionTag/conversionTag.stories.js +47 -0
- package/src/components/tag/conversionTag/index.vue +1 -1
- package/src/components/tag/freeTrialTag/freeTrialTag.stories.js +42 -0
- package/src/components/threeDots/index.vue +20 -3
- package/src/components/threeDots/threeDots.stories.js +59 -0
- package/src/components/videoThumbnail/videoThumbnail.stories.js +1 -1
- package/src/constants/colorPalettes.js +71 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
import Modal from '@/components/modals/modal'
|
|
4
|
+
import theme from '@/assets/theme'
|
|
5
|
+
|
|
6
|
+
jest.mock('@/components/icon/iconCache.mjs', () => ({
|
|
7
|
+
fetchIcon: jest.fn(() => Promise.resolve('mocked-icon-url.svg')),
|
|
8
|
+
}))
|
|
9
|
+
|
|
10
|
+
const mountModal = (options = {}) =>
|
|
11
|
+
mount(Modal, {
|
|
12
|
+
global: {
|
|
13
|
+
provide: {
|
|
14
|
+
theme,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
attachTo: document.body,
|
|
18
|
+
...options,
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
describe('Modal.vue', () => {
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
document.body.innerHTML = ''
|
|
24
|
+
document.body.style.overflow = ''
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('does not render overlay when isOpen is false', async () => {
|
|
28
|
+
const wrapper = mountModal({
|
|
29
|
+
props: { isOpen: false },
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
await wrapper.vm.$nextTick()
|
|
33
|
+
expect(wrapper.find('.rc-modal-wrapper').exists()).toBe(false)
|
|
34
|
+
wrapper.unmount()
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('renders overlay and default slot when open and not loading', async () => {
|
|
38
|
+
const wrapper = mountModal({
|
|
39
|
+
props: { isOpen: true },
|
|
40
|
+
slots: {
|
|
41
|
+
default: '<p data-test-id="modal-slot">Modal body</p>',
|
|
42
|
+
},
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
await wrapper.vm.$nextTick()
|
|
46
|
+
expect(wrapper.find('.rc-modal-wrapper').exists()).toBe(true)
|
|
47
|
+
expect(wrapper.find('[data-test-id="modal-slot"]').exists()).toBe(true)
|
|
48
|
+
expect(wrapper.find('[data-test-id="modal-slot"]').text()).toBe('Modal body')
|
|
49
|
+
expect(wrapper.find('[data-test-id="spinner_container"]').exists()).toBe(false)
|
|
50
|
+
wrapper.unmount()
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('shows spinner and hides slot and close button when isLoading is true', async () => {
|
|
54
|
+
const wrapper = mountModal({
|
|
55
|
+
props: { isOpen: true, isLoading: true },
|
|
56
|
+
slots: {
|
|
57
|
+
default: '<p data-test-id="modal-slot">Should not show</p>',
|
|
58
|
+
},
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
await wrapper.vm.$nextTick()
|
|
62
|
+
expect(wrapper.find('[data-test-id="spinner_container"]').exists()).toBe(true)
|
|
63
|
+
expect(wrapper.find('[data-test-id="modal-slot"]').exists()).toBe(false)
|
|
64
|
+
expect(wrapper.find('.close').exists()).toBe(false)
|
|
65
|
+
wrapper.unmount()
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('hides close button when hideClose is true', async () => {
|
|
69
|
+
const wrapper = mountModal({
|
|
70
|
+
props: { isOpen: true, hideClose: true },
|
|
71
|
+
slots: {
|
|
72
|
+
default: '<span data-test-id="modal-slot-content">Content</span>',
|
|
73
|
+
},
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
await wrapper.vm.$nextTick()
|
|
77
|
+
expect(wrapper.find('[data-test-id="modal-slot-content"]').exists()).toBe(true)
|
|
78
|
+
expect(wrapper.find('.close').exists()).toBe(false)
|
|
79
|
+
wrapper.unmount()
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('emits on-close when close button is clicked', async () => {
|
|
83
|
+
const wrapper = mountModal({
|
|
84
|
+
props: { isOpen: true },
|
|
85
|
+
slots: {
|
|
86
|
+
default: '<span data-test-id="modal-slot-content">Content</span>',
|
|
87
|
+
},
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
await wrapper.vm.$nextTick()
|
|
91
|
+
await wrapper.find('.close').trigger('click')
|
|
92
|
+
await wrapper.vm.$nextTick()
|
|
93
|
+
|
|
94
|
+
expect(wrapper.emitted('on-close')).toHaveLength(1)
|
|
95
|
+
wrapper.unmount()
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('emits on-close when Escape is pressed and closeOnEscape is true', async () => {
|
|
99
|
+
const wrapper = mountModal({
|
|
100
|
+
props: { isOpen: true, closeOnEscape: true },
|
|
101
|
+
slots: {
|
|
102
|
+
default: '<span data-test-id="modal-slot-content">Content</span>',
|
|
103
|
+
},
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
await wrapper.vm.$nextTick()
|
|
107
|
+
window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }))
|
|
108
|
+
await wrapper.vm.$nextTick()
|
|
109
|
+
|
|
110
|
+
expect(wrapper.emitted('on-close')).toHaveLength(1)
|
|
111
|
+
wrapper.unmount()
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it('does not emit on-close on Escape when closeOnEscape is false', async () => {
|
|
115
|
+
const wrapper = mountModal({
|
|
116
|
+
props: { isOpen: true, closeOnEscape: false },
|
|
117
|
+
slots: {
|
|
118
|
+
default: '<span data-test-id="modal-slot-content">Content</span>',
|
|
119
|
+
},
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
await wrapper.vm.$nextTick()
|
|
123
|
+
window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }))
|
|
124
|
+
await wrapper.vm.$nextTick()
|
|
125
|
+
|
|
126
|
+
expect(wrapper.emitted('on-close')).toBeUndefined()
|
|
127
|
+
wrapper.unmount()
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('sets body overflow hidden while open and restores when closed', async () => {
|
|
131
|
+
const wrapper = mountModal({
|
|
132
|
+
props: { isOpen: true },
|
|
133
|
+
slots: {
|
|
134
|
+
default: '<span data-test-id="modal-slot-content">Content</span>',
|
|
135
|
+
},
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
await wrapper.vm.$nextTick()
|
|
139
|
+
expect(document.body.style.overflow).toBe('hidden')
|
|
140
|
+
|
|
141
|
+
await wrapper.setProps({ isOpen: false })
|
|
142
|
+
await wrapper.vm.$nextTick()
|
|
143
|
+
expect(document.body.style.overflow).toBe('')
|
|
144
|
+
|
|
145
|
+
wrapper.unmount()
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it('removes key listener and resets body overflow on unmount while open', async () => {
|
|
149
|
+
const removeSpy = jest.spyOn(window, 'removeEventListener')
|
|
150
|
+
|
|
151
|
+
const wrapper = mountModal({
|
|
152
|
+
props: { isOpen: true },
|
|
153
|
+
slots: {
|
|
154
|
+
default: '<span data-test-id="modal-slot-content">Content</span>',
|
|
155
|
+
},
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
await wrapper.vm.$nextTick()
|
|
159
|
+
expect(document.body.style.overflow).toBe('hidden')
|
|
160
|
+
|
|
161
|
+
wrapper.unmount()
|
|
162
|
+
|
|
163
|
+
expect(removeSpy).toHaveBeenCalledWith('keydown', expect.any(Function))
|
|
164
|
+
expect(document.body.style.overflow).toBe('')
|
|
165
|
+
|
|
166
|
+
removeSpy.mockRestore()
|
|
167
|
+
})
|
|
168
|
+
})
|
|
@@ -1,31 +1,292 @@
|
|
|
1
|
+
import { ref } from 'vue'
|
|
1
2
|
import Modal from './index.vue'
|
|
2
3
|
|
|
3
4
|
export default {
|
|
4
|
-
title: 'Components/Modal',
|
|
5
|
+
title: 'Components/Modals/Modal',
|
|
5
6
|
component: Modal,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: 'fullscreen',
|
|
10
|
+
docs: {
|
|
11
|
+
description: {
|
|
12
|
+
component:
|
|
13
|
+
'Low-level modal shell: backdrop, centered container, optional close control, loading state, and a default slot for any body content. Parent handles layout and actions inside the slot. Emits `on-close` when the close button is used or Escape is pressed (if `closeOnEscape` is true).',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
argTypes: {
|
|
18
|
+
isOpen: {
|
|
19
|
+
control: 'boolean',
|
|
20
|
+
description: 'Renders the modal overlay and panel when true',
|
|
21
|
+
},
|
|
22
|
+
isLoading: {
|
|
23
|
+
control: 'boolean',
|
|
24
|
+
description: 'Shows a large spinner and hides slot content and close button',
|
|
25
|
+
},
|
|
26
|
+
hideClose: {
|
|
27
|
+
control: 'boolean',
|
|
28
|
+
description: 'Hides the corner close button (e.g. forced flows)',
|
|
29
|
+
},
|
|
30
|
+
backdrop: {
|
|
31
|
+
control: 'select',
|
|
32
|
+
options: ['white', 'dark'],
|
|
33
|
+
description: 'Backdrop tint when `fullScreen` is true',
|
|
34
|
+
},
|
|
35
|
+
fullScreen: {
|
|
36
|
+
control: 'boolean',
|
|
37
|
+
description: 'Full-viewport overlay vs inset layout',
|
|
38
|
+
},
|
|
39
|
+
closeOnEscape: {
|
|
40
|
+
control: 'boolean',
|
|
41
|
+
description: 'Close when Escape is pressed',
|
|
42
|
+
},
|
|
43
|
+
stopPropagation: {
|
|
44
|
+
control: 'boolean',
|
|
45
|
+
description: 'Stop click propagation on the modal container',
|
|
46
|
+
},
|
|
47
|
+
overflowRule: {
|
|
48
|
+
control: 'select',
|
|
49
|
+
options: ['auto', 'hidden', 'scroll', 'visible'],
|
|
50
|
+
description: 'CSS overflow on the modal panel',
|
|
51
|
+
},
|
|
52
|
+
maxWidth: {
|
|
53
|
+
control: 'text',
|
|
54
|
+
description: 'Max width of the modal panel (CSS value)',
|
|
55
|
+
},
|
|
56
|
+
addPaddingTop: {
|
|
57
|
+
control: 'boolean',
|
|
58
|
+
description: 'Adds top padding to the overlay (e.g. clear a fixed header)',
|
|
59
|
+
},
|
|
60
|
+
disableDefaultMediaQuery: {
|
|
61
|
+
control: 'boolean',
|
|
62
|
+
description: 'Disables built-in mobile full-width behaviour',
|
|
63
|
+
},
|
|
64
|
+
position: {
|
|
65
|
+
control: 'text',
|
|
66
|
+
description: 'CSS position of the overlay wrapper',
|
|
67
|
+
},
|
|
68
|
+
modalContainerAlignment: {
|
|
69
|
+
control: 'object',
|
|
70
|
+
description: 'Grid alignment `{ alignSelf, justifySelf }` for the panel',
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const sampleBody = `
|
|
76
|
+
<div style="padding: 24px 56px 32px 24px; max-width: 520px;">
|
|
77
|
+
<h2 style="margin: 0 0 12px; font-size: 1.25rem; font-weight: 600;">Edit settings</h2>
|
|
78
|
+
<p style="margin: 0; color: #444; line-height: 1.6;">
|
|
79
|
+
This slot is fully controlled by the parent. Use it for forms, confirmations, or rich content.
|
|
80
|
+
The shell provides backdrop, focus trap basics via overlay, and optional loading UI.
|
|
81
|
+
</p>
|
|
82
|
+
</div>
|
|
83
|
+
`
|
|
84
|
+
|
|
85
|
+
export const Default = {
|
|
86
|
+
args: {
|
|
87
|
+
isOpen: true,
|
|
88
|
+
isLoading: false,
|
|
89
|
+
hideClose: false,
|
|
90
|
+
backdrop: 'dark',
|
|
91
|
+
fullScreen: true,
|
|
92
|
+
closeOnEscape: true,
|
|
93
|
+
stopPropagation: true,
|
|
94
|
+
overflowRule: 'auto',
|
|
95
|
+
maxWidth: '95%',
|
|
96
|
+
addPaddingTop: false,
|
|
97
|
+
disableDefaultMediaQuery: false,
|
|
98
|
+
position: 'fixed',
|
|
99
|
+
modalContainerAlignment: {
|
|
100
|
+
alignSelf: 'center',
|
|
101
|
+
justifySelf: 'center',
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
render: (args) => ({
|
|
105
|
+
components: { Modal },
|
|
106
|
+
setup() {
|
|
107
|
+
return { args }
|
|
108
|
+
},
|
|
109
|
+
template: `
|
|
110
|
+
<Modal v-bind="args" @on-close="() => {}">
|
|
111
|
+
${sampleBody}
|
|
112
|
+
</Modal>
|
|
113
|
+
`,
|
|
114
|
+
}),
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export const Loading = {
|
|
118
|
+
args: {
|
|
119
|
+
...Default.args,
|
|
120
|
+
isLoading: true,
|
|
121
|
+
},
|
|
122
|
+
render: Default.render,
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export const WithoutCloseButton = {
|
|
126
|
+
args: {
|
|
127
|
+
...Default.args,
|
|
128
|
+
hideClose: true,
|
|
129
|
+
},
|
|
130
|
+
render: Default.render,
|
|
131
|
+
parameters: {
|
|
132
|
+
docs: {
|
|
133
|
+
description: {
|
|
134
|
+
story: 'Use when the user must complete an action in the slot (e.g. explicit Save/Cancel inside the body).',
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export const LightBackdrop = {
|
|
141
|
+
args: {
|
|
142
|
+
...Default.args,
|
|
143
|
+
backdrop: 'white',
|
|
144
|
+
},
|
|
145
|
+
render: Default.render,
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export const InsetNotFullScreen = {
|
|
149
|
+
args: {
|
|
150
|
+
...Default.args,
|
|
151
|
+
fullScreen: false,
|
|
152
|
+
backdrop: 'dark',
|
|
153
|
+
maxWidth: '560px',
|
|
154
|
+
},
|
|
155
|
+
render: Default.render,
|
|
156
|
+
parameters: {
|
|
157
|
+
docs: {
|
|
158
|
+
description: {
|
|
159
|
+
story: 'Overlay does not cover the full viewport; useful when embedding preview areas need visible context.',
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export const NarrowPanel = {
|
|
166
|
+
args: {
|
|
167
|
+
...Default.args,
|
|
168
|
+
maxWidth: '380px',
|
|
169
|
+
},
|
|
170
|
+
render: (args) => ({
|
|
171
|
+
components: { Modal },
|
|
172
|
+
setup() {
|
|
173
|
+
return { args }
|
|
174
|
+
},
|
|
175
|
+
template: `
|
|
176
|
+
<Modal v-bind="args" @on-close="() => {}">
|
|
177
|
+
<div style="padding: 24px 56px 28px 24px;">
|
|
178
|
+
<h2 style="margin: 0 0 8px; font-size: 1.1rem;">Confirm</h2>
|
|
179
|
+
<p style="margin: 0; color: #444; font-size: 0.95rem;">
|
|
180
|
+
Delete this item? This cannot be undone.
|
|
181
|
+
</p>
|
|
182
|
+
</div>
|
|
183
|
+
</Modal>
|
|
184
|
+
`,
|
|
185
|
+
}),
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export const ScrollableContent = {
|
|
189
|
+
args: {
|
|
190
|
+
...Default.args,
|
|
191
|
+
maxWidth: '420px',
|
|
192
|
+
overflowRule: 'auto',
|
|
193
|
+
},
|
|
194
|
+
render: (args) => ({
|
|
195
|
+
components: { Modal },
|
|
196
|
+
setup() {
|
|
197
|
+
return { args }
|
|
198
|
+
},
|
|
199
|
+
template: `
|
|
200
|
+
<Modal v-bind="args" @on-close="() => {}">
|
|
201
|
+
<div style="padding: 24px 56px 24px 24px; max-height: 280px;">
|
|
202
|
+
<h2 style="margin: 0 0 12px; font-size: 1.15rem;">Terms</h2>
|
|
203
|
+
<p v-for="n in 8" :key="n" style="margin: 0 0 12px; color: #444; line-height: 1.5;">
|
|
204
|
+
Paragraph {{ n }} — Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio.
|
|
205
|
+
Praesent libero. Sed cursus ante dapibus diam.
|
|
206
|
+
</p>
|
|
207
|
+
</div>
|
|
208
|
+
</Modal>
|
|
209
|
+
`,
|
|
210
|
+
}),
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export const WithTopPadding = {
|
|
214
|
+
args: {
|
|
215
|
+
...Default.args,
|
|
216
|
+
addPaddingTop: true,
|
|
217
|
+
},
|
|
218
|
+
render: Default.render,
|
|
219
|
+
parameters: {
|
|
220
|
+
docs: {
|
|
221
|
+
description: {
|
|
222
|
+
story: 'Extra top padding on the overlay when a fixed app bar occupies the top of the screen.',
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export const InteractiveOpenAndClose = {
|
|
229
|
+
render: () => ({
|
|
230
|
+
components: { Modal },
|
|
231
|
+
setup() {
|
|
232
|
+
const isOpen = ref(false)
|
|
233
|
+
const openModal = () => {
|
|
234
|
+
isOpen.value = true
|
|
235
|
+
}
|
|
236
|
+
const closeModal = () => {
|
|
237
|
+
isOpen.value = false
|
|
238
|
+
}
|
|
239
|
+
return { isOpen, openModal, closeModal }
|
|
240
|
+
},
|
|
241
|
+
template: `
|
|
242
|
+
<div
|
|
243
|
+
style="
|
|
244
|
+
min-height: 100vh;
|
|
245
|
+
padding: 24px;
|
|
246
|
+
background: linear-gradient(160deg, #e8ecf1 0%, #dce3ea 100%);
|
|
247
|
+
font-family: system-ui, sans-serif;
|
|
248
|
+
"
|
|
249
|
+
>
|
|
250
|
+
<p style="margin: 0 0 16px; color: #333;">
|
|
251
|
+
Open the modal, then use the close button or Escape to dismiss.
|
|
252
|
+
</p>
|
|
253
|
+
<button
|
|
254
|
+
type="button"
|
|
255
|
+
style="
|
|
256
|
+
padding: 10px 18px;
|
|
257
|
+
border-radius: 6px;
|
|
258
|
+
border: 1px solid #2563eb;
|
|
259
|
+
background: #2563eb;
|
|
260
|
+
color: white;
|
|
261
|
+
font-weight: 600;
|
|
262
|
+
cursor: pointer;
|
|
263
|
+
"
|
|
264
|
+
@click="openModal"
|
|
265
|
+
>
|
|
266
|
+
Open modal
|
|
267
|
+
</button>
|
|
268
|
+
<Modal
|
|
269
|
+
:is-open="isOpen"
|
|
270
|
+
backdrop="dark"
|
|
271
|
+
:full-screen="true"
|
|
272
|
+
max-width="480px"
|
|
273
|
+
@on-close="closeModal"
|
|
274
|
+
>
|
|
275
|
+
<div style="padding: 24px 56px 28px 24px;">
|
|
276
|
+
<h2 style="margin: 0 0 10px; font-size: 1.2rem;">Dismiss me</h2>
|
|
277
|
+
<p style="margin: 0; color: #444; line-height: 1.55;">
|
|
278
|
+
Closing updates local state in the story so you can open again from the button.
|
|
279
|
+
</p>
|
|
280
|
+
</div>
|
|
281
|
+
</Modal>
|
|
282
|
+
</div>
|
|
283
|
+
`,
|
|
284
|
+
}),
|
|
285
|
+
parameters: {
|
|
286
|
+
docs: {
|
|
287
|
+
description: {
|
|
288
|
+
story: 'Minimal host page with local `isOpen` state to demonstrate open/close behaviour.',
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
},
|
|
31
292
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<BodyElement>
|
|
3
|
+
<slot></slot>
|
|
4
|
+
</BodyElement>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script>
|
|
8
|
+
// To use:
|
|
9
|
+
// import ModalBody from "@eturnity/eturnity_reusable_components/src/components/modals/modalBody"
|
|
10
|
+
// <modal-body>{{ $gettext('body text') }}</modal-Body>
|
|
11
|
+
|
|
12
|
+
import styled from 'vue3-styled-components'
|
|
13
|
+
|
|
14
|
+
const BodyElement = styled.div`
|
|
15
|
+
color: ${(props) => props.theme.semanticColors.teal[800]};
|
|
16
|
+
font-family: inherit;
|
|
17
|
+
font-size: 12px;
|
|
18
|
+
font-style: normal;
|
|
19
|
+
font-weight: 400;
|
|
20
|
+
line-height: 150%;
|
|
21
|
+
letter-spacing: 0;
|
|
22
|
+
`
|
|
23
|
+
|
|
24
|
+
export default {
|
|
25
|
+
name: 'ModalBody',
|
|
26
|
+
components: {
|
|
27
|
+
BodyElement,
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
</script>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<ButtonsContainer :stretch="stretch">
|
|
3
|
+
<slot></slot>
|
|
4
|
+
</ButtonsContainer>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script>
|
|
8
|
+
import styled from 'vue3-styled-components'
|
|
9
|
+
|
|
10
|
+
const buttonsContainerAttrs = {
|
|
11
|
+
stretch: Boolean,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const ButtonsContainer = styled('div', buttonsContainerAttrs)`
|
|
15
|
+
display: flex;
|
|
16
|
+
justify-content: ${(props) => (props.stretch ? 'flex-start' : 'flex-end')};
|
|
17
|
+
gap: 8px;
|
|
18
|
+
|
|
19
|
+
${(props) =>
|
|
20
|
+
props.stretch &&
|
|
21
|
+
`
|
|
22
|
+
width: 100%;
|
|
23
|
+
> * {
|
|
24
|
+
flex: 1 1 0;
|
|
25
|
+
min-width: 0;
|
|
26
|
+
}
|
|
27
|
+
`}
|
|
28
|
+
`
|
|
29
|
+
|
|
30
|
+
export default {
|
|
31
|
+
name: 'ModalButtonContainer',
|
|
32
|
+
components: {
|
|
33
|
+
ButtonsContainer,
|
|
34
|
+
},
|
|
35
|
+
props: {
|
|
36
|
+
stretch: {
|
|
37
|
+
type: Boolean,
|
|
38
|
+
default: false,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
}
|
|
42
|
+
</script>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import ModalButtonContainer from './index.vue'
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
title: 'Components/Modals/ModalButtonContainer',
|
|
5
|
+
component: ModalButtonContainer,
|
|
6
|
+
tags: ['autodocs'],
|
|
7
|
+
argTypes: {
|
|
8
|
+
stretch: {
|
|
9
|
+
control: 'boolean',
|
|
10
|
+
description:
|
|
11
|
+
'When true, children buttons stretch to fill the container width.',
|
|
12
|
+
},
|
|
13
|
+
buttons: {
|
|
14
|
+
control: { type: 'range', min: 1, max: 3, step: 1 },
|
|
15
|
+
description: 'Number of buttons rendered in the slot (1 to 3).',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const Template = (args) => ({
|
|
21
|
+
components: { ModalButtonContainer },
|
|
22
|
+
setup() {
|
|
23
|
+
const labels = ['Cancel', 'Save', 'Confirm']
|
|
24
|
+
return { args, labels }
|
|
25
|
+
},
|
|
26
|
+
template: `
|
|
27
|
+
<div style="width: 420px; border: 1px dashed #cbd5e1; padding: 12px;">
|
|
28
|
+
<ModalButtonContainer :stretch="args.stretch">
|
|
29
|
+
<button
|
|
30
|
+
v-for="(label, idx) in labels.slice(0, args.buttons)"
|
|
31
|
+
:key="idx"
|
|
32
|
+
style="padding: 8px 12px; border: 1px solid #94a3b8; border-radius: 6px; background: #fff;"
|
|
33
|
+
type="button"
|
|
34
|
+
>
|
|
35
|
+
{{ label }}
|
|
36
|
+
</button>
|
|
37
|
+
</ModalButtonContainer>
|
|
38
|
+
</div>
|
|
39
|
+
`,
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
export const Minimal1Button = Template.bind({})
|
|
43
|
+
Minimal1Button.args = { stretch: false, buttons: 1 }
|
|
44
|
+
|
|
45
|
+
export const Minimal2Buttons = Template.bind({})
|
|
46
|
+
Minimal2Buttons.args = { stretch: false, buttons: 2 }
|
|
47
|
+
|
|
48
|
+
export const Minimal3Buttons = Template.bind({})
|
|
49
|
+
Minimal3Buttons.args = { stretch: false, buttons: 3 }
|
|
50
|
+
|
|
51
|
+
export const Stretch1Button = Template.bind({})
|
|
52
|
+
Stretch1Button.args = { stretch: true, buttons: 1 }
|
|
53
|
+
|
|
54
|
+
export const Stretch2Buttons = Template.bind({})
|
|
55
|
+
Stretch2Buttons.args = { stretch: true, buttons: 2 }
|
|
56
|
+
|
|
57
|
+
export const Stretch3Buttons = Template.bind({})
|
|
58
|
+
Stretch3Buttons.args = { stretch: true, buttons: 3 }
|
|
59
|
+
|