@redsift/popovers 9.2.3-patch → 9.2.3
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/coverage/clover.xml +763 -0
- package/coverage/coverage-final.json +53 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/dialog/Dialog.tsx.html +271 -0
- package/coverage/lcov-report/dialog/context.ts.html +97 -0
- package/coverage/lcov-report/dialog/index.html +191 -0
- package/coverage/lcov-report/dialog/index.ts.html +100 -0
- package/coverage/lcov-report/dialog/types.ts.html +241 -0
- package/coverage/lcov-report/dialog/useDialog.tsx.html +346 -0
- package/coverage/lcov-report/dialog/useDialogContext.tsx.html +121 -0
- package/coverage/lcov-report/dialog-content/DialogContent.tsx.html +484 -0
- package/coverage/lcov-report/dialog-content/index.html +146 -0
- package/coverage/lcov-report/dialog-content/index.ts.html +91 -0
- package/coverage/lcov-report/dialog-content/intl/index.html +116 -0
- package/coverage/lcov-report/dialog-content/intl/index.ts.html +106 -0
- package/coverage/lcov-report/dialog-content/styles.ts.html +256 -0
- package/coverage/lcov-report/dialog-content-actions/DialogContentActions.tsx.html +205 -0
- package/coverage/lcov-report/dialog-content-actions/index.html +146 -0
- package/coverage/lcov-report/dialog-content-actions/index.ts.html +91 -0
- package/coverage/lcov-report/dialog-content-actions/styles.ts.html +139 -0
- package/coverage/lcov-report/dialog-content-body/DialogContentBody.tsx.html +232 -0
- package/coverage/lcov-report/dialog-content-body/index.html +146 -0
- package/coverage/lcov-report/dialog-content-body/index.ts.html +91 -0
- package/coverage/lcov-report/dialog-content-body/styles.ts.html +259 -0
- package/coverage/lcov-report/dialog-content-header/DialogContentHeader.tsx.html +280 -0
- package/coverage/lcov-report/dialog-content-header/index.html +146 -0
- package/coverage/lcov-report/dialog-content-header/index.ts.html +91 -0
- package/coverage/lcov-report/dialog-content-header/styles.ts.html +193 -0
- package/coverage/lcov-report/dialog-trigger/DialogTrigger.tsx.html +217 -0
- package/coverage/lcov-report/dialog-trigger/index.html +131 -0
- package/coverage/lcov-report/dialog-trigger/index.ts.html +91 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +341 -0
- package/coverage/lcov-report/popover/Popover.tsx.html +295 -0
- package/coverage/lcov-report/popover/context.ts.html +97 -0
- package/coverage/lcov-report/popover/index.html +191 -0
- package/coverage/lcov-report/popover/index.ts.html +100 -0
- package/coverage/lcov-report/popover/types.ts.html +283 -0
- package/coverage/lcov-report/popover/usePopover.tsx.html +415 -0
- package/coverage/lcov-report/popover/usePopoverContext.tsx.html +121 -0
- package/coverage/lcov-report/popover-content/PopoverContent.tsx.html +229 -0
- package/coverage/lcov-report/popover-content/index.html +146 -0
- package/coverage/lcov-report/popover-content/index.ts.html +94 -0
- package/coverage/lcov-report/popover-content/styles.ts.html +370 -0
- package/coverage/lcov-report/popover-trigger/PopoverTrigger.tsx.html +202 -0
- package/coverage/lcov-report/popover-trigger/index.html +131 -0
- package/coverage/lcov-report/popover-trigger/index.ts.html +91 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +196 -0
- package/coverage/lcov-report/toast/Toast.tsx.html +373 -0
- package/coverage/lcov-report/toast/index.html +161 -0
- package/coverage/lcov-report/toast/index.ts.html +91 -0
- package/coverage/lcov-report/toast/intl/index.html +116 -0
- package/coverage/lcov-report/toast/intl/index.ts.html +106 -0
- package/coverage/lcov-report/toast/styles.ts.html +193 -0
- package/coverage/lcov-report/toast/types.ts.html +217 -0
- package/coverage/lcov-report/toast-container/ToastContainer.tsx.html +217 -0
- package/coverage/lcov-report/toast-container/index.html +161 -0
- package/coverage/lcov-report/toast-container/index.ts.html +94 -0
- package/coverage/lcov-report/toast-container/styles.ts.html +2284 -0
- package/coverage/lcov-report/toast-container/useToast.tsx.html +469 -0
- package/coverage/lcov-report/tooltip/Tooltip.tsx.html +250 -0
- package/coverage/lcov-report/tooltip/context.ts.html +97 -0
- package/coverage/lcov-report/tooltip/index.html +191 -0
- package/coverage/lcov-report/tooltip/index.ts.html +100 -0
- package/coverage/lcov-report/tooltip/types.ts.html +250 -0
- package/coverage/lcov-report/tooltip/useTooltip.tsx.html +358 -0
- package/coverage/lcov-report/tooltip/useTooltipContext.tsx.html +121 -0
- package/coverage/lcov-report/tooltip-content/TooltipContent.tsx.html +313 -0
- package/coverage/lcov-report/tooltip-content/index.html +146 -0
- package/coverage/lcov-report/tooltip-content/index.ts.html +91 -0
- package/coverage/lcov-report/tooltip-content/styles.ts.html +337 -0
- package/coverage/lcov-report/tooltip-trigger/TooltipTrigger.tsx.html +211 -0
- package/coverage/lcov-report/tooltip-trigger/index.html +131 -0
- package/coverage/lcov-report/tooltip-trigger/index.ts.html +91 -0
- package/coverage/lcov.info +1510 -0
- package/coverage/storybook/coverage-storybook.json +58724 -0
- package/dist/package.json +96 -0
- package/index.ts +1 -0
- package/jest.config.js +3 -0
- package/package.json +2 -3
- package/rollup.config.js +13 -0
- package/src/components/dialog/Dialog.stories.tsx +264 -0
- package/src/components/dialog/Dialog.test.tsx +116 -0
- package/src/components/dialog/Dialog.tsx +62 -0
- package/src/components/dialog/context.ts +4 -0
- package/src/components/dialog/index.ts +5 -0
- package/src/components/dialog/types.ts +52 -0
- package/src/components/dialog/useDialog.tsx +87 -0
- package/src/components/dialog/useDialogContext.tsx +12 -0
- package/src/components/dialog-content/DialogContent.stories.tsx +348 -0
- package/src/components/dialog-content/DialogContent.tsx +133 -0
- package/src/components/dialog-content/index.ts +2 -0
- package/src/components/dialog-content/intl/en-US.json +3 -0
- package/src/components/dialog-content/intl/fr-FR.json +3 -0
- package/src/components/dialog-content/intl/index.ts +7 -0
- package/src/components/dialog-content/styles.ts +57 -0
- package/src/components/dialog-content/types.ts +10 -0
- package/src/components/dialog-content-actions/DialogContentActions.test.tsx +68 -0
- package/src/components/dialog-content-actions/DialogContentActions.tsx +40 -0
- package/src/components/dialog-content-actions/index.ts +2 -0
- package/src/components/dialog-content-actions/styles.ts +18 -0
- package/src/components/dialog-content-actions/types.ts +11 -0
- package/src/components/dialog-content-body/DialogContentBody.test.tsx +63 -0
- package/src/components/dialog-content-body/DialogContentBody.tsx +49 -0
- package/src/components/dialog-content-body/index.ts +2 -0
- package/src/components/dialog-content-body/styles.ts +58 -0
- package/src/components/dialog-content-body/types.ts +14 -0
- package/src/components/dialog-content-header/DialogContentHeader.test.tsx +63 -0
- package/src/components/dialog-content-header/DialogContentHeader.tsx +65 -0
- package/src/components/dialog-content-header/index.ts +2 -0
- package/src/components/dialog-content-header/styles.ts +36 -0
- package/src/components/dialog-content-header/types.ts +21 -0
- package/src/components/dialog-trigger/DialogTrigger.tsx +44 -0
- package/src/components/dialog-trigger/index.ts +2 -0
- package/src/components/dialog-trigger/types.ts +9 -0
- package/src/components/popover/Popover.stories.tsx +129 -0
- package/src/components/popover/Popover.test.tsx +102 -0
- package/src/components/popover/Popover.tsx +70 -0
- package/src/components/popover/context.ts +4 -0
- package/src/components/popover/index.ts +5 -0
- package/src/components/popover/types.ts +66 -0
- package/src/components/popover/usePopover.tsx +110 -0
- package/src/components/popover/usePopoverContext.tsx +12 -0
- package/src/components/popover-content/PopoverContent.tsx +48 -0
- package/src/components/popover-content/index.ts +3 -0
- package/src/components/popover-content/styles.ts +95 -0
- package/src/components/popover-content/types.ts +11 -0
- package/src/components/popover-trigger/PopoverTrigger.tsx +39 -0
- package/src/components/popover-trigger/index.ts +2 -0
- package/src/components/popover-trigger/types.ts +9 -0
- package/src/components/toast/Toast.stories.tsx +68 -0
- package/src/components/toast/Toast.test.tsx +63 -0
- package/src/components/toast/Toast.tsx +96 -0
- package/src/components/toast/index.ts +2 -0
- package/src/components/toast/intl/en-US.json +3 -0
- package/src/components/toast/intl/fr-FR.json +3 -0
- package/src/components/toast/intl/index.ts +7 -0
- package/src/components/toast/styles.ts +36 -0
- package/src/components/toast/types.ts +44 -0
- package/src/components/toast-container/ToastContainer.stories.tsx +349 -0
- package/src/components/toast-container/ToastContainer.tsx +44 -0
- package/src/components/toast-container/index.ts +3 -0
- package/src/components/toast-container/styles.ts +733 -0
- package/src/components/toast-container/types.ts +110 -0
- package/src/components/toast-container/useToast.test.tsx +111 -0
- package/src/components/toast-container/useToast.tsx +128 -0
- package/src/components/tooltip/Tooltip.stories.tsx +196 -0
- package/src/components/tooltip/Tooltip.test.tsx +119 -0
- package/src/components/tooltip/Tooltip.tsx +55 -0
- package/src/components/tooltip/context.ts +4 -0
- package/src/components/tooltip/index.ts +5 -0
- package/src/components/tooltip/types.ts +55 -0
- package/src/components/tooltip/useTooltip.tsx +93 -0
- package/src/components/tooltip/useTooltipContext.tsx +12 -0
- package/src/components/tooltip-content/TooltipContent.tsx +76 -0
- package/src/components/tooltip-content/index.ts +2 -0
- package/src/components/tooltip-content/styles.ts +84 -0
- package/src/components/tooltip-content/types.ts +14 -0
- package/src/components/tooltip-trigger/TooltipTrigger.tsx +42 -0
- package/src/components/tooltip-trigger/index.ts +2 -0
- package/src/components/tooltip-trigger/types.ts +9 -0
- package/src/index.ts +16 -0
- package/tsconfig.json +3 -0
- /package/{CONTRIBUTING.md → dist/CONTRIBUTING.md} +0 -0
- /package/{index.d.ts → dist/index.d.ts} +0 -0
- /package/{index.js → dist/index.js} +0 -0
- /package/{index.js.map → dist/index.js.map} +0 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { act, render, waitFor } from '@testing-library/react';
|
|
3
|
+
|
|
4
|
+
import { DialogContentActions } from '.';
|
|
5
|
+
import { Dialog } from '../dialog';
|
|
6
|
+
|
|
7
|
+
const DialogActionsWrapper = ({ children }: { children: React.ReactNode }) => {
|
|
8
|
+
return (
|
|
9
|
+
<Dialog isOpen>
|
|
10
|
+
<Dialog.Content>{children}</Dialog.Content>
|
|
11
|
+
</Dialog>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
describe('DialogContentActions', () => {
|
|
16
|
+
it('handles default', async () => {
|
|
17
|
+
const { getByText } = render(
|
|
18
|
+
<DialogContentActions>Actions</DialogContentActions>,
|
|
19
|
+
{
|
|
20
|
+
wrapper: DialogActionsWrapper,
|
|
21
|
+
}
|
|
22
|
+
);
|
|
23
|
+
await act(async () => {});
|
|
24
|
+
await waitFor(() => {
|
|
25
|
+
expect(getByText('Actions')).toBeVisible();
|
|
26
|
+
});
|
|
27
|
+
expect(getByText('Actions')).toBeVisible();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('supports custom className', async () => {
|
|
31
|
+
const { getByText } = render(
|
|
32
|
+
<DialogContentActions className="test-class">
|
|
33
|
+
Actions
|
|
34
|
+
</DialogContentActions>,
|
|
35
|
+
{ wrapper: DialogActionsWrapper }
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
await act(async () => {});
|
|
39
|
+
await waitFor(() => {
|
|
40
|
+
expect(getByText('Actions')).toBeVisible();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const component = getByText('Actions');
|
|
44
|
+
expect(component).toHaveAttribute(
|
|
45
|
+
'class',
|
|
46
|
+
expect.stringContaining(DialogContentActions.className!)
|
|
47
|
+
);
|
|
48
|
+
expect(component).toHaveAttribute(
|
|
49
|
+
'class',
|
|
50
|
+
expect.stringContaining('test-class')
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('supports custom data attributes', async () => {
|
|
55
|
+
const { getByText } = render(
|
|
56
|
+
<DialogContentActions data-testid="test">Actions</DialogContentActions>,
|
|
57
|
+
{ wrapper: DialogActionsWrapper }
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
await act(async () => {});
|
|
61
|
+
await waitFor(() => {
|
|
62
|
+
expect(getByText('Actions')).toBeVisible();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const component = getByText('Actions');
|
|
66
|
+
expect(component).toHaveAttribute('data-testid', 'test');
|
|
67
|
+
});
|
|
68
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import { useMergeRefs } from '@floating-ui/react';
|
|
4
|
+
|
|
5
|
+
import { Comp } from '@redsift/design-system';
|
|
6
|
+
import { StyledDialogContentActions } from './styles';
|
|
7
|
+
import { DialogContentActionsProps } from './types';
|
|
8
|
+
import { useDialogContext } from '../dialog';
|
|
9
|
+
|
|
10
|
+
const COMPONENT_NAME = 'DialogContentActions';
|
|
11
|
+
const CLASSNAME = 'redsift-dialog-content-actions';
|
|
12
|
+
const DEFAULT_PROPS: Partial<DialogContentActionsProps> = {
|
|
13
|
+
flexDirection: 'row',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The DialogContentActions component.
|
|
18
|
+
*/
|
|
19
|
+
export const DialogContentActions: Comp<
|
|
20
|
+
DialogContentActionsProps,
|
|
21
|
+
HTMLDivElement
|
|
22
|
+
> = forwardRef((props, ref) => {
|
|
23
|
+
const { children, className, ...forwardedProps } = props;
|
|
24
|
+
|
|
25
|
+
const { actionsRef, initialFocus } = useDialogContext();
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<StyledDialogContentActions
|
|
29
|
+
{...forwardedProps}
|
|
30
|
+
className={classNames(DialogContentActions.className, className)}
|
|
31
|
+
ref={useMergeRefs([ref, actionsRef])}
|
|
32
|
+
tabIndex={initialFocus === 'actions' ? -1 : undefined}
|
|
33
|
+
>
|
|
34
|
+
{children}
|
|
35
|
+
</StyledDialogContentActions>
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
DialogContentActions.className = CLASSNAME;
|
|
39
|
+
DialogContentActions.defaultProps = DEFAULT_PROPS;
|
|
40
|
+
DialogContentActions.displayName = COMPONENT_NAME;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import { baseContainer } from '@redsift/design-system';
|
|
3
|
+
import { StyledDialogContentActionsProps } from './types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Component style.
|
|
7
|
+
*/
|
|
8
|
+
export const StyledDialogContentActions = styled.div<StyledDialogContentActionsProps>`
|
|
9
|
+
display: flex;
|
|
10
|
+
${baseContainer}
|
|
11
|
+
|
|
12
|
+
margin-top: 8px;
|
|
13
|
+
margin-bottom: 8px;
|
|
14
|
+
|
|
15
|
+
&:focus-visible {
|
|
16
|
+
outline: none;
|
|
17
|
+
}
|
|
18
|
+
`;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ComponentProps } from 'react';
|
|
2
|
+
import { ContainerProps } from '@redsift/design-system';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Component props.
|
|
6
|
+
*/
|
|
7
|
+
export interface DialogContentActionsProps
|
|
8
|
+
extends ComponentProps<'div'>,
|
|
9
|
+
ContainerProps {}
|
|
10
|
+
|
|
11
|
+
export type StyledDialogContentActionsProps = DialogContentActionsProps;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { act, render, waitFor } from '@testing-library/react';
|
|
3
|
+
|
|
4
|
+
import { DialogContentBody } from '.';
|
|
5
|
+
import { Dialog } from '../dialog';
|
|
6
|
+
|
|
7
|
+
const DialogBodyWrapper = ({ children }: { children: React.ReactNode }) => {
|
|
8
|
+
return (
|
|
9
|
+
<Dialog isOpen>
|
|
10
|
+
<Dialog.Content>{children}</Dialog.Content>
|
|
11
|
+
</Dialog>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
describe('DialogContentBody', () => {
|
|
16
|
+
it('handles default', async () => {
|
|
17
|
+
const { getByText } = render(<DialogContentBody>Body</DialogContentBody>, {
|
|
18
|
+
wrapper: DialogBodyWrapper,
|
|
19
|
+
});
|
|
20
|
+
await act(async () => {});
|
|
21
|
+
await waitFor(() => {
|
|
22
|
+
expect(getByText('Body')).toBeVisible();
|
|
23
|
+
});
|
|
24
|
+
expect(getByText('Body')).toBeVisible();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('supports custom className', async () => {
|
|
28
|
+
const { getByText } = render(
|
|
29
|
+
<DialogContentBody className="test-class">Body</DialogContentBody>,
|
|
30
|
+
{ wrapper: DialogBodyWrapper }
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
await act(async () => {});
|
|
34
|
+
await waitFor(() => {
|
|
35
|
+
expect(getByText('Body')).toBeVisible();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const component = getByText('Body');
|
|
39
|
+
expect(component).toHaveAttribute(
|
|
40
|
+
'class',
|
|
41
|
+
expect.stringContaining(DialogContentBody.className!)
|
|
42
|
+
);
|
|
43
|
+
expect(component).toHaveAttribute(
|
|
44
|
+
'class',
|
|
45
|
+
expect.stringContaining('test-class')
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('supports custom data attributes', async () => {
|
|
50
|
+
const { getByText } = render(
|
|
51
|
+
<DialogContentBody data-testid="test">Body</DialogContentBody>,
|
|
52
|
+
{ wrapper: DialogBodyWrapper }
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
await act(async () => {});
|
|
56
|
+
await waitFor(() => {
|
|
57
|
+
expect(getByText('Body')).toBeVisible();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const component = getByText('Body');
|
|
61
|
+
expect(component).toHaveAttribute('data-testid', 'test');
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import { useMergeRefs } from '@floating-ui/react';
|
|
4
|
+
|
|
5
|
+
import { Comp, useBoundingClientRect } from '@redsift/design-system';
|
|
6
|
+
import { StyledDialogContentBody } from './styles';
|
|
7
|
+
import { DialogContentBodyProps } from './types';
|
|
8
|
+
import { useDialogContext } from '../dialog';
|
|
9
|
+
|
|
10
|
+
const COMPONENT_NAME = 'DialogContentBody';
|
|
11
|
+
const CLASSNAME = 'redsift-dialog-content-body';
|
|
12
|
+
const DEFAULT_PROPS: Partial<DialogContentBodyProps> = {};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The DialogContentBody component.
|
|
16
|
+
*/
|
|
17
|
+
export const DialogContentBody: Comp<DialogContentBodyProps, HTMLDivElement> =
|
|
18
|
+
forwardRef((props, ref) => {
|
|
19
|
+
const { children, className, ...forwardedProps } = props;
|
|
20
|
+
|
|
21
|
+
const { actionsRef, bodyRef, hasCloseButton, headerRef, initialFocus } =
|
|
22
|
+
useDialogContext();
|
|
23
|
+
|
|
24
|
+
const { height: headerHeight = 0 } = useBoundingClientRect(
|
|
25
|
+
headerRef as any,
|
|
26
|
+
[children, headerRef]
|
|
27
|
+
);
|
|
28
|
+
const { height: actionsHeight = 0 } = useBoundingClientRect(
|
|
29
|
+
actionsRef as any,
|
|
30
|
+
[children, actionsRef]
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
/* istanbul ignore next */
|
|
34
|
+
return (
|
|
35
|
+
<StyledDialogContentBody
|
|
36
|
+
{...forwardedProps}
|
|
37
|
+
className={classNames(DialogContentBody.className, className)}
|
|
38
|
+
ref={useMergeRefs([ref, bodyRef])}
|
|
39
|
+
tabIndex={initialFocus === 'body' ? -1 : undefined}
|
|
40
|
+
$marginTop={Math.max(headerHeight, hasCloseButton ? 40 : 0) + 80}
|
|
41
|
+
$marginBottom={(actionsHeight === 0 ? 0 : actionsHeight + 16) + 80}
|
|
42
|
+
>
|
|
43
|
+
{children}
|
|
44
|
+
</StyledDialogContentBody>
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
DialogContentBody.className = CLASSNAME;
|
|
48
|
+
DialogContentBody.defaultProps = DEFAULT_PROPS;
|
|
49
|
+
DialogContentBody.displayName = COMPONENT_NAME;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import styled, { css } from 'styled-components';
|
|
2
|
+
import { baseContainer } from '@redsift/design-system';
|
|
3
|
+
import { StyledDialogContentBodyProps } from './types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Component style.
|
|
7
|
+
*/
|
|
8
|
+
export const StyledDialogContentBody = styled.div<StyledDialogContentBodyProps>`
|
|
9
|
+
${baseContainer}
|
|
10
|
+
|
|
11
|
+
overflow-y: auto;
|
|
12
|
+
${({ $marginTop, $marginBottom }) => css`
|
|
13
|
+
max-height: calc(100vh - ${$marginTop + $marginBottom}px);
|
|
14
|
+
`}
|
|
15
|
+
|
|
16
|
+
color: var(--redsift-color-neutral-darkgrey);
|
|
17
|
+
font-family: var(--redsift-typography-body-font-family);
|
|
18
|
+
font-size: var(--redsift-typography-body-font-size);
|
|
19
|
+
font-weight: var(--redsift-typography-body-font-weight);
|
|
20
|
+
line-height: var(--redsift-typography-body-line-height);
|
|
21
|
+
padding: 0 24px;
|
|
22
|
+
margin: 16px -24px;
|
|
23
|
+
|
|
24
|
+
&:focus-visible {
|
|
25
|
+
outline: none;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
scrollbar-color: var(--redsift-color-neutral-midgrey) transparent;
|
|
29
|
+
scrollbar-width: thin;
|
|
30
|
+
|
|
31
|
+
&::-webkit-scrollbar {
|
|
32
|
+
block-size: 9px;
|
|
33
|
+
inline-size: 9px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
&::-webkit-scrollbar-thumb {
|
|
37
|
+
background-clip: padding-box;
|
|
38
|
+
background-color: var(--redsift-color-neutral-midgrey);
|
|
39
|
+
border-radius: 0px;
|
|
40
|
+
border-left: 2px solid transparent;
|
|
41
|
+
border-right: 2px solid transparent;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
&::-webkit-scrollbar-track {
|
|
45
|
+
background-color: transparent;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
&::-webkit-scrollbar-thumb:horizontal,
|
|
49
|
+
&::-webkit-scrollbar-thumb:vertical {
|
|
50
|
+
&:hover {
|
|
51
|
+
background-color: var(--redsift-color-neutral-darkgrey);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
&::-webkit-scrollbar-corner {
|
|
56
|
+
visibility: hidden;
|
|
57
|
+
}
|
|
58
|
+
`;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ComponentProps } from 'react';
|
|
2
|
+
import { ContainerProps } from '@redsift/design-system';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Component props.
|
|
6
|
+
*/
|
|
7
|
+
export interface DialogContentBodyProps
|
|
8
|
+
extends ComponentProps<'div'>,
|
|
9
|
+
ContainerProps {}
|
|
10
|
+
|
|
11
|
+
export type StyledDialogContentBodyProps = DialogContentBodyProps & {
|
|
12
|
+
$marginTop: number;
|
|
13
|
+
$marginBottom: number;
|
|
14
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { act, render, waitFor } from '@testing-library/react';
|
|
3
|
+
|
|
4
|
+
import { DialogContentHeader } from '.';
|
|
5
|
+
import { Dialog } from '../dialog';
|
|
6
|
+
|
|
7
|
+
const DialogHeaderWrapper = ({ children }: { children: React.ReactNode }) => {
|
|
8
|
+
return (
|
|
9
|
+
<Dialog isOpen>
|
|
10
|
+
<Dialog.Content>{children}</Dialog.Content>
|
|
11
|
+
</Dialog>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
describe('DialogContentHeader', () => {
|
|
16
|
+
it('handles default', async () => {
|
|
17
|
+
const { getByText } = render(<DialogContentHeader header="Header" />, {
|
|
18
|
+
wrapper: DialogHeaderWrapper,
|
|
19
|
+
});
|
|
20
|
+
await act(async () => {});
|
|
21
|
+
await waitFor(() => {
|
|
22
|
+
expect(getByText('Header')).toBeVisible();
|
|
23
|
+
});
|
|
24
|
+
expect(getByText('Header')).toBeVisible();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('supports custom className', async () => {
|
|
28
|
+
const { getByText } = render(
|
|
29
|
+
<DialogContentHeader className="test-class" header="Header" />,
|
|
30
|
+
{ wrapper: DialogHeaderWrapper }
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
await act(async () => {});
|
|
34
|
+
await waitFor(() => {
|
|
35
|
+
expect(getByText('Header')).toBeVisible();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const component = getByText('Header').parentElement;
|
|
39
|
+
expect(component).toHaveAttribute(
|
|
40
|
+
'class',
|
|
41
|
+
expect.stringContaining(DialogContentHeader.className!)
|
|
42
|
+
);
|
|
43
|
+
expect(component).toHaveAttribute(
|
|
44
|
+
'class',
|
|
45
|
+
expect.stringContaining('test-class')
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('supports custom data attributes', async () => {
|
|
50
|
+
const { getByText } = render(
|
|
51
|
+
<DialogContentHeader data-testid="test" header="Header" />,
|
|
52
|
+
{ wrapper: DialogHeaderWrapper }
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
await act(async () => {});
|
|
56
|
+
await waitFor(() => {
|
|
57
|
+
expect(getByText('Header')).toBeVisible();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const component = getByText('Header').parentElement;
|
|
61
|
+
expect(component).toHaveAttribute('data-testid', 'test');
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import { useMergeRefs } from '@floating-ui/react';
|
|
4
|
+
|
|
5
|
+
import { Icon, Heading, Comp, useId } from '@redsift/design-system';
|
|
6
|
+
import { StyledDialogContentHeader } from './styles';
|
|
7
|
+
import { DialogContentHeaderProps } from './types';
|
|
8
|
+
import { useDialogContext } from '../dialog';
|
|
9
|
+
|
|
10
|
+
const COMPONENT_NAME = 'DialogContentHeader';
|
|
11
|
+
const CLASSNAME = 'redsift-dialog-content-header';
|
|
12
|
+
const DEFAULT_PROPS: Partial<DialogContentHeaderProps> = {};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The DialogContentHeader component.
|
|
16
|
+
*/
|
|
17
|
+
export const DialogContentHeader: Comp<DialogContentHeaderProps, HTMLDivElement> = forwardRef((props, ref) => {
|
|
18
|
+
const { children, className, header, headingProps, icon, subheader, ...forwardedProps } = props;
|
|
19
|
+
|
|
20
|
+
const { setLabelId, setDescriptionId, headerRef, initialFocus } = useDialogContext();
|
|
21
|
+
const [labelId] = useId();
|
|
22
|
+
const [descriptionId] = useId();
|
|
23
|
+
|
|
24
|
+
// Only sets `aria-labelledby` on the Dialog root element
|
|
25
|
+
// if this component is mounted inside it.
|
|
26
|
+
React.useLayoutEffect(() => {
|
|
27
|
+
setLabelId(labelId);
|
|
28
|
+
return () => setLabelId(undefined);
|
|
29
|
+
}, [labelId, setLabelId]);
|
|
30
|
+
|
|
31
|
+
// Only sets `aria-describedby` on the Dialog root element
|
|
32
|
+
// if this component is mounted inside it.
|
|
33
|
+
React.useLayoutEffect(() => {
|
|
34
|
+
setDescriptionId(descriptionId);
|
|
35
|
+
return () => setDescriptionId(undefined);
|
|
36
|
+
}, [descriptionId, setDescriptionId]);
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<StyledDialogContentHeader
|
|
40
|
+
{...forwardedProps}
|
|
41
|
+
className={classNames(DialogContentHeader.className, className)}
|
|
42
|
+
ref={useMergeRefs([ref, headerRef])}
|
|
43
|
+
tabIndex={initialFocus === 'header' ? -1 : undefined}
|
|
44
|
+
>
|
|
45
|
+
{subheader ? <div className={`${DialogContentHeader.className}__subheader`}>{subheader}</div> : null}
|
|
46
|
+
{header ? (
|
|
47
|
+
<Heading
|
|
48
|
+
as="h2"
|
|
49
|
+
color="black"
|
|
50
|
+
className={`${DialogContentHeader.className}__header`}
|
|
51
|
+
id={labelId}
|
|
52
|
+
variant="h2"
|
|
53
|
+
{...headingProps}
|
|
54
|
+
>
|
|
55
|
+
{icon ? <Icon icon={icon} aria-hidden="true" /> : null}
|
|
56
|
+
{header}
|
|
57
|
+
</Heading>
|
|
58
|
+
) : null}
|
|
59
|
+
{children ? <div id={descriptionId}>{children}</div> : null}
|
|
60
|
+
</StyledDialogContentHeader>
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
DialogContentHeader.className = CLASSNAME;
|
|
64
|
+
DialogContentHeader.defaultProps = DEFAULT_PROPS;
|
|
65
|
+
DialogContentHeader.displayName = COMPONENT_NAME;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import { StyledDialogContentHeaderProps } from './types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Component style.
|
|
6
|
+
*/
|
|
7
|
+
export const StyledDialogContentHeader = styled.div<StyledDialogContentHeaderProps>`
|
|
8
|
+
color: var(--redsift-color-neutral-black);
|
|
9
|
+
|
|
10
|
+
&:focus-visible {
|
|
11
|
+
outline: none;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.redsift-dialog-content-header__header {
|
|
15
|
+
align-items: center;
|
|
16
|
+
display: flex;
|
|
17
|
+
gap: 8px;
|
|
18
|
+
padding: 10px 0px;
|
|
19
|
+
margin: 0px;
|
|
20
|
+
|
|
21
|
+
.redsift-icon {
|
|
22
|
+
color: var(--redsift-color-neutral-black);
|
|
23
|
+
padding-right: 8px;
|
|
24
|
+
font-size: 30px;
|
|
25
|
+
line-height: 30px;
|
|
26
|
+
height: 30px;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.redsift-dialog-content-header__subheader {
|
|
31
|
+
font-family: var(--redsift-typography-body-font-family);
|
|
32
|
+
font-size: var(--redsift-typography-body-font-size);
|
|
33
|
+
font-weight: var(--redsift-typography-body-font-weight);
|
|
34
|
+
line-height: var(--redsift-typography-body-line-height);
|
|
35
|
+
}
|
|
36
|
+
`;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ComponentProps } from 'react';
|
|
2
|
+
import { HeadingProps, IconProps } from '@redsift/design-system';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Component props.
|
|
6
|
+
*/
|
|
7
|
+
export interface DialogContentHeaderProps extends ComponentProps<'div'> {
|
|
8
|
+
/** Header. */
|
|
9
|
+
header?: string;
|
|
10
|
+
/** Heading props allowing to override the component rendered by the heading without changing its style. */
|
|
11
|
+
headingProps?: Pick<HeadingProps, 'as' | 'noWrap'>;
|
|
12
|
+
/** Subheader */
|
|
13
|
+
subheader?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Can be a string or an array of strings containing `d` property of the `path` SVG element.<br />
|
|
16
|
+
* Can also be a ReactElement.
|
|
17
|
+
*/
|
|
18
|
+
icon?: IconProps['icon'];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type StyledDialogContentHeaderProps = DialogContentHeaderProps;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React, { forwardRef, ReactElement } from 'react';
|
|
2
|
+
import { useMergeRefs } from '@floating-ui/react';
|
|
3
|
+
|
|
4
|
+
import { Comp, isComponent } from '@redsift/design-system';
|
|
5
|
+
import { useDialogContext } from '../dialog';
|
|
6
|
+
import { DialogTriggerProps } from './types';
|
|
7
|
+
|
|
8
|
+
const COMPONENT_NAME = 'DialogTrigger';
|
|
9
|
+
const CLASSNAME = 'redsift-dialog-trigger';
|
|
10
|
+
const DEFAULT_PROPS: Partial<DialogTriggerProps> = {};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The DialogTrigger component.
|
|
14
|
+
*/
|
|
15
|
+
export const DialogTrigger: Comp<DialogTriggerProps, HTMLButtonElement> = forwardRef((props, ref) => {
|
|
16
|
+
const { children } = props;
|
|
17
|
+
|
|
18
|
+
const { getReferenceProps, isOpen, handleOpen, refs } = useDialogContext();
|
|
19
|
+
const childrenRef = (children as any).ref;
|
|
20
|
+
const triggerRef = useMergeRefs([refs.setReference, ref, childrenRef]);
|
|
21
|
+
|
|
22
|
+
if (
|
|
23
|
+
isComponent('Button')(children) ||
|
|
24
|
+
isComponent('IconButton')(children) ||
|
|
25
|
+
isComponent('LinkButton')(children) ||
|
|
26
|
+
isComponent('Checkbox')(children)
|
|
27
|
+
) {
|
|
28
|
+
return React.cloneElement(children as ReactElement, {
|
|
29
|
+
...getReferenceProps({
|
|
30
|
+
ref: triggerRef,
|
|
31
|
+
...props,
|
|
32
|
+
...children.props,
|
|
33
|
+
children: children.props.children ?? '',
|
|
34
|
+
}),
|
|
35
|
+
onClick: () => handleOpen(!isOpen),
|
|
36
|
+
isActive: isOpen,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return <>{children}</>;
|
|
41
|
+
});
|
|
42
|
+
DialogTrigger.className = CLASSNAME;
|
|
43
|
+
DialogTrigger.defaultProps = DEFAULT_PROPS;
|
|
44
|
+
DialogTrigger.displayName = COMPONENT_NAME;
|