@moda/om 19.2.0 → 19.4.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/README.md +6 -6
- package/dist/index.cjs.js +2632 -2172
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +2631 -2174
- package/dist/index.esm.js.map +1 -1
- package/dist/src/components/Breakpoint/Breakpoint.d.ts +6 -0
- package/dist/src/components/Confirm/Confirm.d.ts +32 -0
- package/dist/src/components/Confirm/Confirm.stories.d.ts +5 -0
- package/dist/src/components/Confirm/Confirm.test.d.ts +1 -0
- package/dist/src/components/Confirm/index.d.ts +1 -0
- package/dist/src/components/Dialog/Dialog.d.ts +9 -0
- package/dist/src/components/Dialog/Dialog.stories.d.ts +6 -0
- package/dist/src/components/Dialog/Dialog.test.d.ts +1 -0
- package/dist/src/components/Dialog/index.d.ts +1 -0
- package/dist/src/components/Select/Select.d.ts +1 -0
- package/dist/src/components/index.d.ts +7 -5
- package/dist/styles.css +1 -1
- package/package.json +91 -88
- package/src/components/Breakpoint/Breakpoint.tsx +3 -3
- package/src/components/Confirm/Confirm.stories.tsx +35 -0
- package/src/components/Confirm/Confirm.test.tsx +44 -0
- package/src/components/Confirm/Confirm.tsx +90 -0
- package/src/components/Confirm/index.ts +1 -0
- package/src/components/Dialog/Dialog.scss +15 -0
- package/src/components/Dialog/Dialog.stories.tsx +28 -0
- package/src/components/Dialog/Dialog.test.tsx +38 -0
- package/src/components/Dialog/Dialog.tsx +33 -0
- package/src/components/Dialog/index.ts +1 -0
- package/src/components/Select/Select.tsx +1 -0
- package/src/components/index.ts +7 -5
- package/CHANGELOG.md +0 -764
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, waitForElementToBeRemoved } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
|
|
5
|
+
import { ConfirmProvider, useConfirm } from './Confirm';
|
|
6
|
+
|
|
7
|
+
const ConfirmComponent: React.FC = () => {
|
|
8
|
+
const { confirm } = useConfirm();
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<button onClick={() => confirm({ title: 'Are you sure?', confirmationText: 'Yes' })}>
|
|
12
|
+
Click Me
|
|
13
|
+
</button>
|
|
14
|
+
);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
describe('Dialog', () => {
|
|
18
|
+
it('should show the confirmation and hide it on confirm/cancel', async () => {
|
|
19
|
+
render(
|
|
20
|
+
<ConfirmProvider>
|
|
21
|
+
<ConfirmComponent />
|
|
22
|
+
</ConfirmProvider>
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
// Should not show by default
|
|
26
|
+
expect(screen.queryByText('Are you sure?')).not.toBeInTheDocument();
|
|
27
|
+
|
|
28
|
+
// Should show after clicking button
|
|
29
|
+
await userEvent.click(screen.getByRole('button', { name: 'Click Me' }));
|
|
30
|
+
await screen.findByText('Are you sure?');
|
|
31
|
+
|
|
32
|
+
// Should hide after clicking Yes
|
|
33
|
+
await userEvent.click(screen.getByRole('button', { name: 'Yes' }));
|
|
34
|
+
await waitForElementToBeRemoved(() => screen.queryByText('Are you sure?'));
|
|
35
|
+
|
|
36
|
+
// Should show after clicking button
|
|
37
|
+
await userEvent.click(screen.getByRole('button', { name: 'Click Me' }));
|
|
38
|
+
await screen.findByText('Are you sure?');
|
|
39
|
+
|
|
40
|
+
// Should hide after clicking Cancel
|
|
41
|
+
await userEvent.click(screen.getByRole('button', { name: 'Cancel' }));
|
|
42
|
+
await waitForElementToBeRemoved(() => screen.queryByText('Are you sure?'));
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import React, { ReactNode, createContext, useCallback, useContext, useMemo, useState } from 'react';
|
|
2
|
+
import { Dialog } from '../Dialog';
|
|
3
|
+
import { Button } from '../Button';
|
|
4
|
+
|
|
5
|
+
export type TConfirmOptions = {
|
|
6
|
+
title: ReactNode;
|
|
7
|
+
message: ReactNode;
|
|
8
|
+
confirmationText: ReactNode;
|
|
9
|
+
cancellationText: ReactNode;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type TConfirm = (options?: Partial<TConfirmOptions>) => Promise<boolean>;
|
|
13
|
+
|
|
14
|
+
const DEFAULT_OPTIONS: TConfirmOptions = {
|
|
15
|
+
title: 'Are you sure?',
|
|
16
|
+
message: '',
|
|
17
|
+
confirmationText: 'Ok',
|
|
18
|
+
cancellationText: 'Cancel'
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const ConfirmContext = createContext<{ confirm: TConfirm }>({
|
|
22
|
+
confirm: () => Promise.resolve(false)
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export const ConfirmProvider: React.FC<{ children?: ReactNode }> = ({ children }) => {
|
|
26
|
+
const [options, setOptions] = useState<Partial<TConfirmOptions>>({});
|
|
27
|
+
const [resolve, setResolve] = useState<((confirmed: boolean) => void) | null>(null);
|
|
28
|
+
|
|
29
|
+
const { title, message, confirmationText, cancellationText } = { ...DEFAULT_OPTIONS, ...options };
|
|
30
|
+
|
|
31
|
+
const confirm = useCallback(
|
|
32
|
+
(options?: Partial<TConfirmOptions>) =>
|
|
33
|
+
new Promise<boolean>(resolve => {
|
|
34
|
+
setOptions(options ?? {});
|
|
35
|
+
setResolve(() => resolve);
|
|
36
|
+
}),
|
|
37
|
+
[]
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const handleCancel = useCallback(() => {
|
|
41
|
+
resolve?.(false);
|
|
42
|
+
setResolve(null);
|
|
43
|
+
}, [resolve]);
|
|
44
|
+
|
|
45
|
+
const handleConfirm = useCallback(() => {
|
|
46
|
+
resolve?.(true);
|
|
47
|
+
setResolve(null);
|
|
48
|
+
}, [resolve]);
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<ConfirmContext.Provider value={useMemo(() => ({ confirm }), [confirm])}>
|
|
52
|
+
{children}
|
|
53
|
+
<Dialog
|
|
54
|
+
show={resolve != null}
|
|
55
|
+
onClose={handleCancel}
|
|
56
|
+
title={title}
|
|
57
|
+
message={message}
|
|
58
|
+
actions={
|
|
59
|
+
<>
|
|
60
|
+
{cancellationText && (
|
|
61
|
+
<Button secondary onClick={handleCancel}>
|
|
62
|
+
{cancellationText}
|
|
63
|
+
</Button>
|
|
64
|
+
)}
|
|
65
|
+
<Button onClick={handleConfirm}>{confirmationText}</Button>
|
|
66
|
+
</>
|
|
67
|
+
}
|
|
68
|
+
/>
|
|
69
|
+
</ConfirmContext.Provider>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Shows a confirmation dialog and returns a Promise<boolean> representing the user choice (resolves with true on confirmation and with false on cancellation).
|
|
75
|
+
*
|
|
76
|
+
* Inspired by https://www.npmjs.com/package/material-ui-confirm
|
|
77
|
+
*
|
|
78
|
+
* Usage:
|
|
79
|
+
* ```
|
|
80
|
+
* const {confirm} = useConfirm();
|
|
81
|
+
*
|
|
82
|
+
* confirm({
|
|
83
|
+
* title: 'Are you sure you want to delete this?',
|
|
84
|
+
* confirmationText: 'Delete'
|
|
85
|
+
* }).then(confirmed => {
|
|
86
|
+
* // ...
|
|
87
|
+
* });
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export const useConfirm = () => useContext(ConfirmContext);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Confirm';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { Button } from '../Button';
|
|
4
|
+
import { Dialog } from './Dialog';
|
|
5
|
+
|
|
6
|
+
export default { title: 'Components/Dialog' };
|
|
7
|
+
|
|
8
|
+
enum Mode {
|
|
9
|
+
Resting,
|
|
10
|
+
Open
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const Default = () => {
|
|
14
|
+
const [mode, setMode] = useState(Mode.Resting);
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<>
|
|
18
|
+
<Button onClick={() => setMode(Mode.Open)}>Open Dialog</Button>
|
|
19
|
+
<Dialog
|
|
20
|
+
show={mode === Mode.Open}
|
|
21
|
+
onClose={() => setMode(Mode.Resting)}
|
|
22
|
+
title='Exclusive Offer'
|
|
23
|
+
message='Get 20% off your next purchase when you sign up for our newsletter today. Be the first to know about our latest products, promotions, and exclusive deals!'
|
|
24
|
+
actions={<Button onClick={() => setMode(Mode.Resting)}>OK</Button>}
|
|
25
|
+
/>
|
|
26
|
+
</>
|
|
27
|
+
);
|
|
28
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { render, screen, waitForElementToBeRemoved } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
|
|
5
|
+
import { Dialog } from './Dialog';
|
|
6
|
+
|
|
7
|
+
const DialogComponent: React.FC = () => {
|
|
8
|
+
const [showDialog, setShowDialog] = useState(false);
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<>
|
|
12
|
+
<button onClick={() => setShowDialog(true)}>Click Me</button>
|
|
13
|
+
<Dialog
|
|
14
|
+
show={showDialog}
|
|
15
|
+
onClose={() => setShowDialog(false)}
|
|
16
|
+
title='This is a dialog'
|
|
17
|
+
actions={<button onClick={() => setShowDialog(false)}>Close</button>}
|
|
18
|
+
/>
|
|
19
|
+
</>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
describe('Dialog', () => {
|
|
24
|
+
it('should open and close the dialog', async () => {
|
|
25
|
+
render(<DialogComponent />);
|
|
26
|
+
|
|
27
|
+
// Should not show by default
|
|
28
|
+
expect(screen.queryByText('This is a dialog')).not.toBeInTheDocument();
|
|
29
|
+
|
|
30
|
+
// Should show after clicking button
|
|
31
|
+
await userEvent.click(screen.getByRole('button', { name: 'Click Me' }));
|
|
32
|
+
await screen.findByText('This is a dialog');
|
|
33
|
+
|
|
34
|
+
// Should hide after clicking Close
|
|
35
|
+
await userEvent.click(screen.getByRole('button', { name: 'Close' }));
|
|
36
|
+
await waitForElementToBeRemoved(() => screen.queryByText('This is a dialog'));
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import { ModalOverlay, ModalOverlayProps } from '../ModalOverlay';
|
|
4
|
+
import { Stack } from '../Stack';
|
|
5
|
+
import { Text } from '../Text';
|
|
6
|
+
|
|
7
|
+
import './Dialog.scss';
|
|
8
|
+
|
|
9
|
+
export type DialogProps = Omit<ModalOverlayProps, 'title'> & {
|
|
10
|
+
title?: ReactNode;
|
|
11
|
+
message?: ReactNode;
|
|
12
|
+
actions?: ReactNode;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const Dialog: React.FC<DialogProps> = ({
|
|
16
|
+
title,
|
|
17
|
+
message,
|
|
18
|
+
actions,
|
|
19
|
+
contentClassName,
|
|
20
|
+
...rest
|
|
21
|
+
}) => (
|
|
22
|
+
<ModalOverlay contentClassName={classNames('Dialog', contentClassName)} {...rest}>
|
|
23
|
+
<Stack className='Dialog__content' space={6}>
|
|
24
|
+
{title && <Text treatment='h5'>{title}</Text>}
|
|
25
|
+
{message && <Text>{message}</Text>}
|
|
26
|
+
{actions && (
|
|
27
|
+
<Stack direction='horizontal' space={3} alignItems='center' justifyContent='center'>
|
|
28
|
+
{actions}
|
|
29
|
+
</Stack>
|
|
30
|
+
)}
|
|
31
|
+
</Stack>
|
|
32
|
+
</ModalOverlay>
|
|
33
|
+
);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Dialog';
|
package/src/components/index.ts
CHANGED
|
@@ -6,13 +6,15 @@ export * from './Button';
|
|
|
6
6
|
export * from './Checkbox';
|
|
7
7
|
export * from './Clickable';
|
|
8
8
|
export * from './ColorSwatch';
|
|
9
|
-
export * from './
|
|
9
|
+
export * from './Confirm';
|
|
10
10
|
export * from './Constrain';
|
|
11
|
+
export * from './ControlLink';
|
|
11
12
|
export * from './CreditCardNumberInput';
|
|
12
13
|
export * from './DefinitionList';
|
|
14
|
+
export * from './Dialog';
|
|
15
|
+
export * from './Divider';
|
|
13
16
|
export * from './Expandable';
|
|
14
17
|
export * from './Field';
|
|
15
|
-
export * from './Divider';
|
|
16
18
|
export * from './Label';
|
|
17
19
|
export * from './Loading';
|
|
18
20
|
export * from './LoadingBalls';
|
|
@@ -22,6 +24,7 @@ export * from './ModalOverlay';
|
|
|
22
24
|
export * from './NavigationPreventer';
|
|
23
25
|
export * from './Overlay';
|
|
24
26
|
export * from './Paginator';
|
|
27
|
+
export * from './PasswordInput';
|
|
25
28
|
export * from './Popover';
|
|
26
29
|
export * from './PromoBanner';
|
|
27
30
|
export * from './RadioButton';
|
|
@@ -29,13 +32,12 @@ export * from './SearchInput';
|
|
|
29
32
|
export * from './Select';
|
|
30
33
|
export * from './SelectableButton';
|
|
31
34
|
export * from './Shape';
|
|
35
|
+
export * from './SlidingPane';
|
|
32
36
|
export * from './Stack';
|
|
33
|
-
export * from './Tag';
|
|
34
37
|
export * from './Tabs';
|
|
38
|
+
export * from './Tag';
|
|
35
39
|
export * from './Text';
|
|
36
40
|
export * from './Textarea';
|
|
37
41
|
export * from './TextInput';
|
|
38
42
|
export * from './Toast';
|
|
39
43
|
export * from './VerticalDivider';
|
|
40
|
-
export * from './PasswordInput';
|
|
41
|
-
export * from './SlidingPane';
|