agroptima-design-system 1.2.38-beta.2 → 1.2.38-beta.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agroptima-design-system",
3
- "version": "1.2.38-beta.2",
3
+ "version": "1.2.38-beta.4",
4
4
  "scripts": {
5
5
  "dev": "npm run storybook",
6
6
  "storybook": "storybook dev -p 6006 --ci",
@@ -4,7 +4,8 @@ import React, { useEffect, useRef } from 'react'
4
4
  import { classNames } from '../../utils/classNames'
5
5
  import type { Variant } from './Modal'
6
6
 
7
- export interface ModalDialogProps extends React.HTMLAttributes<HTMLDialogElement> {
7
+ export interface ModalDialogProps
8
+ extends React.HTMLAttributes<HTMLDialogElement> {
8
9
  isOpen?: boolean
9
10
  onClose?: () => void
10
11
  details?: boolean
@@ -29,12 +30,12 @@ export function ModalDialog({
29
30
  }
30
31
 
31
32
  useEffect(() => {
33
+ // if (!isOpen) return
34
+
32
35
  keepScrollbarSpace()
33
- window.addEventListener('resize', keepScrollbarSpace)
34
36
 
35
37
  return () => {
36
38
  removeScrollbarSpace()
37
- window.removeEventListener('resize', keepScrollbarSpace)
38
39
  }
39
40
  }, [])
40
41
 
@@ -6,7 +6,8 @@ import { Meta } from "@storybook/addon-docs/blocks";
6
6
 
7
7
  ## 1.2.38
8
8
 
9
- * Add CheckboxWithDivider component
9
+ * Fix Modal scroll lock not being cleared when the component stays mounted and `isOpen` toggles to `false`
10
+ * Fix Modal scroll lock width flickering on window resize while open, caused by recalculating the scrollbar width after `overflow: hidden` had already removed it
10
11
 
11
12
  ## 1.2.37
12
13
 
@@ -37,7 +37,7 @@ const meta = {
37
37
  ' <li>Require a response from the user</li>' +
38
38
  ' <li>Notify the user of any related information</li>' +
39
39
  ' <li>Confirm a user decision</li>' +
40
- " <li>It's opened/closed through `isOpen` prop. If we don't want it to be part of the DOM, we can also add a conditional render on the frontend project.</li>" +
40
+ " <li>It's opened/closed through `isOpen` prop — the component can be left mounted and toggled. If we don't want it to be part of the DOM at all (e.g. to avoid rendering its contents until needed), we can also add a conditional render on the frontend project.</li>" +
41
41
  ' <li>Natively, focus is set on the first nested focusable element and explicitly indicated by default by the browser</li>' +
42
42
  " <li>When nesting a Form inside Modal component, remember to add `type='button'` to all Cancel buttons to not to be considered as submitable</li>" +
43
43
  '</ul>',
@@ -145,4 +145,88 @@ describe('Modal', () => {
145
145
  expect(screen.queryAllByRole('option')).toHaveLength(3)
146
146
  expect(screen.getByRole('option', { name: 'Item 3' })).toBeInTheDocument()
147
147
  })
148
+
149
+ it('clears the scroll lock when isOpen toggles to false while the modal stays mounted', async () => {
150
+ const originalInnerWidth = window.innerWidth
151
+ Object.defineProperty(window, 'innerWidth', {
152
+ configurable: true,
153
+ value: 1024,
154
+ })
155
+ Object.defineProperty(document.documentElement, 'clientWidth', {
156
+ configurable: true,
157
+ value: 1000,
158
+ })
159
+
160
+ const user = userEvent.setup()
161
+ const ToggleableModal = () => {
162
+ const [isOpen, setIsOpen] = React.useState(true)
163
+
164
+ return (
165
+ <Modal
166
+ id="scroll-lock-modal"
167
+ isOpen={isOpen}
168
+ title="Scroll lock modal"
169
+ buttons={[
170
+ {
171
+ label: 'Close',
172
+ onClick: () => setIsOpen(false),
173
+ },
174
+ ]}
175
+ >
176
+ content
177
+ </Modal>
178
+ )
179
+ }
180
+
181
+ render(<ToggleableModal />)
182
+
183
+ expect(document.documentElement).toHaveClass('modal-scroll')
184
+
185
+ await user.click(screen.getByRole('button', { name: 'Close' }))
186
+
187
+ expect(document.documentElement).not.toHaveClass('modal-scroll')
188
+
189
+ Object.defineProperty(window, 'innerWidth', {
190
+ configurable: true,
191
+ value: originalInnerWidth,
192
+ })
193
+ })
194
+
195
+ it('does not recalculate the scroll lock width on window resize while open', () => {
196
+ const originalInnerWidth = window.innerWidth
197
+ Object.defineProperty(window, 'innerWidth', {
198
+ configurable: true,
199
+ value: 1024,
200
+ })
201
+ Object.defineProperty(document.documentElement, 'clientWidth', {
202
+ configurable: true,
203
+ value: 1000,
204
+ })
205
+
206
+ render(
207
+ <Modal id="resize-modal" isOpen={true} title="Resize modal" buttons={[]}>
208
+ content
209
+ </Modal>,
210
+ )
211
+
212
+ expect(document.documentElement).toHaveClass('modal-scroll')
213
+ expect(document.documentElement.style.getPropertyValue('--modal-scroll-width')).toBe('24px')
214
+
215
+ // Simulates the browser removing the native scrollbar once `overflow: hidden`
216
+ // is applied, which previously made the resize listener think the scrollbar
217
+ // was gone and drop the lock.
218
+ Object.defineProperty(document.documentElement, 'clientWidth', {
219
+ configurable: true,
220
+ value: 1024,
221
+ })
222
+ window.dispatchEvent(new Event('resize'))
223
+
224
+ expect(document.documentElement).toHaveClass('modal-scroll')
225
+ expect(document.documentElement.style.getPropertyValue('--modal-scroll-width')).toBe('24px')
226
+
227
+ Object.defineProperty(window, 'innerWidth', {
228
+ configurable: true,
229
+ value: originalInnerWidth,
230
+ })
231
+ })
148
232
  })
@@ -1,17 +0,0 @@
1
- @use '../../settings/typography/content' as typography;
2
- @use '../../settings/config';
3
-
4
- .checkbox-with-divider {
5
- display: flex;
6
- align-items: center;
7
- gap: config.$space-3x;
8
-
9
- .form-label {
10
- @include typography.body-bold;
11
- }
12
-
13
- .long.line {
14
- flex: 1;
15
- border: 1px solid var(--neutral-color-200);
16
- }
17
- }
@@ -1,31 +0,0 @@
1
- import './CheckboxWithDivider.scss'
2
- import type { CheckboxProps } from '../Checkbox'
3
- import { Checkbox } from '../Checkbox'
4
-
5
- export interface CheckboxWithDividerProps extends Omit<
6
- CheckboxProps,
7
- 'checked'
8
- > {
9
- title: string
10
- checked: boolean
11
- }
12
-
13
- export function CheckboxWithDivider({
14
- title,
15
- accessibilityLabel,
16
- className,
17
- ...props
18
- }: CheckboxWithDividerProps) {
19
- return (
20
- <div className="checkbox-with-divider">
21
- <Checkbox
22
- accessibilityLabel={accessibilityLabel || title}
23
- className={className}
24
- {...props}
25
- >
26
- {title}
27
- </Checkbox>
28
- <div className="long line" />
29
- </div>
30
- )
31
- }
@@ -1,4 +0,0 @@
1
- import { CheckboxWithDivider } from './CheckboxWithDivider'
2
-
3
- export type { CheckboxWithDividerProps } from './CheckboxWithDivider'
4
- export { CheckboxWithDivider }
@@ -1,48 +0,0 @@
1
- import type { StoryObj } from '@storybook/nextjs'
2
- import { CheckboxWithDivider } from '../atoms/CheckboxWithDivider'
3
-
4
- const meta = {
5
- title: 'Design System/Atoms/CheckboxWithDivider',
6
- component: CheckboxWithDivider,
7
- parameters: {
8
- docs: {
9
- description: {
10
- component:
11
- '<h2>Usage guidelines</h2>' +
12
- '<p>A CheckboxWithDivider combines a Checkbox with a divider line, used as a selectable group header (e.g. "select all" on a list of items).</p>',
13
- },
14
- },
15
- },
16
- tags: ['autodocs', 'Layout components'],
17
- }
18
-
19
- export default meta
20
- type Story = StoryObj<typeof meta>
21
-
22
- export const Default: Story = {
23
- args: {
24
- title: '19/01/2025 - My gaming diary',
25
- name: 'select-all',
26
- checked: false,
27
- onChange: () => console.log('change'),
28
- },
29
- }
30
-
31
- export const Checked: Story = {
32
- args: {
33
- title: '19/01/2025 - My gaming diary',
34
- name: 'select-all',
35
- checked: true,
36
- onChange: () => console.log('change'),
37
- },
38
- }
39
-
40
- export const Indeterminate: Story = {
41
- args: {
42
- title: '19/01/2025 - My gaming diary',
43
- name: 'select-all',
44
- checked: true,
45
- indeterminate: true,
46
- onChange: () => console.log('change'),
47
- },
48
- }
@@ -1,64 +0,0 @@
1
- import { render } from '@testing-library/react'
2
- import userEvent from '@testing-library/user-event'
3
- import { CheckboxWithDivider } from '../src/atoms/CheckboxWithDivider'
4
-
5
- describe('CheckboxWithDivider', () => {
6
- it('renders the title and a divider line', () => {
7
- const { getByRole, getByText, container } = render(
8
- <CheckboxWithDivider
9
- title="19/01/2025 - My gaming diary"
10
- name="select-all"
11
- checked={false}
12
- />,
13
- )
14
-
15
- expect(getByRole('checkbox')).toBeInTheDocument()
16
- expect(getByText('19/01/2025 - My gaming diary')).toBeInTheDocument()
17
- expect(container.querySelector('.long.line')).toBeInTheDocument()
18
- })
19
-
20
- it('toggles when clicking the title, thanks to the native label', async () => {
21
- const user = userEvent.setup()
22
- const handleChange = jest.fn()
23
-
24
- const { getByText } = render(
25
- <CheckboxWithDivider
26
- title="19/01/2025 - My gaming diary"
27
- name="select-all"
28
- checked={false}
29
- onChange={handleChange}
30
- />,
31
- )
32
-
33
- await user.click(getByText('19/01/2025 - My gaming diary'))
34
-
35
- expect(handleChange).toHaveBeenCalledTimes(1)
36
- })
37
-
38
- it('renders as indeterminate', () => {
39
- const { getByRole } = render(
40
- <CheckboxWithDivider
41
- title="19/01/2025 - My gaming diary"
42
- name="select-all"
43
- checked
44
- indeterminate
45
- />,
46
- )
47
-
48
- expect(getByRole('checkbox')).toBeChecked()
49
- })
50
-
51
- it('falls back to the title as the accessible name', () => {
52
- const { getByRole } = render(
53
- <CheckboxWithDivider
54
- title="19/01/2025 - My gaming diary"
55
- name="select-all"
56
- checked={false}
57
- />,
58
- )
59
-
60
- expect(
61
- getByRole('checkbox', { name: '19/01/2025 - My gaming diary' }),
62
- ).toBeInTheDocument()
63
- })
64
- })