@frontify/guideline-blocks-settings 0.32.1 → 0.33.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.
- package/CHANGELOG.md +147 -0
- package/dist/components/Attachments/AttachmentItem.es.js +128 -109
- package/dist/components/Attachments/AttachmentItem.es.js.map +1 -1
- package/dist/components/Attachments/Attachments.es.js +9 -1
- package/dist/components/Attachments/Attachments.es.js.map +1 -1
- package/dist/components/Attachments/AttachmentsButtonTrigger.es.js +17 -9
- package/dist/components/Attachments/AttachmentsButtonTrigger.es.js.map +1 -1
- package/dist/components/BlockItemWrapper/BlockItemWrapper.es.js +38 -44
- package/dist/components/BlockItemWrapper/BlockItemWrapper.es.js.map +1 -1
- package/dist/components/BlockItemWrapper/Toolbar/AttachmentsToolbarButton/AttachmentsToolbarButton.es.js +33 -0
- package/dist/components/BlockItemWrapper/Toolbar/AttachmentsToolbarButton/AttachmentsToolbarButton.es.js.map +1 -0
- package/dist/components/BlockItemWrapper/Toolbar/AttachmentsToolbarButton/AttachmentsToolbarButtonTrigger.es.js +26 -0
- package/dist/components/BlockItemWrapper/Toolbar/AttachmentsToolbarButton/AttachmentsToolbarButtonTrigger.es.js.map +1 -0
- package/dist/components/BlockItemWrapper/Toolbar/BaseToolbarButton.es.js +28 -0
- package/dist/components/BlockItemWrapper/Toolbar/BaseToolbarButton.es.js.map +1 -0
- package/dist/components/BlockItemWrapper/Toolbar/DragHandleToolbarButton/DragHandleToolbarButton.es.js +35 -0
- package/dist/components/BlockItemWrapper/Toolbar/DragHandleToolbarButton/DragHandleToolbarButton.es.js.map +1 -0
- package/dist/components/BlockItemWrapper/Toolbar/FlyoutToolbarButton/FlyoutToolbarButton.es.js +44 -0
- package/dist/components/BlockItemWrapper/Toolbar/FlyoutToolbarButton/FlyoutToolbarButton.es.js.map +1 -0
- package/dist/components/BlockItemWrapper/Toolbar/MenuToolbarButton/MenuToolbarButton.es.js +25 -0
- package/dist/components/BlockItemWrapper/Toolbar/MenuToolbarButton/MenuToolbarButton.es.js.map +1 -0
- package/dist/components/BlockItemWrapper/Toolbar/MenuToolbarButton/ToolbarFlyoutMenu.es.js +29 -0
- package/dist/components/BlockItemWrapper/Toolbar/MenuToolbarButton/ToolbarFlyoutMenu.es.js.map +1 -0
- package/dist/components/BlockItemWrapper/Toolbar/Toolbar.es.js +11 -112
- package/dist/components/BlockItemWrapper/Toolbar/Toolbar.es.js.map +1 -1
- package/dist/components/BlockItemWrapper/Toolbar/ToolbarButton/ToolbarButton.es.js +12 -0
- package/dist/components/BlockItemWrapper/Toolbar/ToolbarButton/ToolbarButton.es.js.map +1 -0
- package/dist/components/BlockItemWrapper/Toolbar/ToolbarButtonTooltip.es.js +20 -0
- package/dist/components/BlockItemWrapper/Toolbar/ToolbarButtonTooltip.es.js.map +1 -0
- package/dist/components/BlockItemWrapper/Toolbar/context/DragPreviewContext.es.js +11 -0
- package/dist/components/BlockItemWrapper/Toolbar/context/DragPreviewContext.es.js.map +1 -0
- package/dist/components/BlockItemWrapper/Toolbar/context/MultiFlyoutContext.es.js +18 -0
- package/dist/components/BlockItemWrapper/Toolbar/context/MultiFlyoutContext.es.js.map +1 -0
- package/dist/components/BlockItemWrapper/Toolbar/helpers.es.js +1 -1
- package/dist/components/BlockItemWrapper/Toolbar/helpers.es.js.map +1 -1
- package/dist/components/BlockItemWrapper/Toolbar/hooks/useMultiFlyoutState.es.js +18 -0
- package/dist/components/BlockItemWrapper/Toolbar/hooks/useMultiFlyoutState.es.js.map +1 -0
- package/dist/helpers/mapColorPalettes.es.js +20 -8
- package/dist/helpers/mapColorPalettes.es.js.map +1 -1
- package/dist/index.cjs.js +3 -3
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +92 -27
- package/dist/index.es.js +205 -187
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +3 -3
- package/dist/index.umd.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +4 -2
- package/setupTests.ts +7 -2
- package/src/components/Attachments/AttachmentItem.tsx +21 -0
- package/src/components/Attachments/Attachments.spec.ct.tsx +20 -1
- package/src/components/Attachments/Attachments.tsx +8 -4
- package/src/components/Attachments/AttachmentsButtonTrigger.tsx +11 -3
- package/src/components/Attachments/types.ts +4 -2
- package/src/components/BlockItemWrapper/BlockItemWrapper.spec.ct.tsx +37 -38
- package/src/components/BlockItemWrapper/BlockItemWrapper.tsx +44 -48
- package/src/components/BlockItemWrapper/Toolbar/AttachmentsToolbarButton/AttachmentsToolbarButton.spec.tsx +96 -0
- package/src/components/BlockItemWrapper/Toolbar/AttachmentsToolbarButton/AttachmentsToolbarButton.tsx +42 -0
- package/src/components/BlockItemWrapper/Toolbar/AttachmentsToolbarButton/AttachmentsToolbarButtonTrigger.spec.tsx +44 -0
- package/src/components/BlockItemWrapper/Toolbar/AttachmentsToolbarButton/AttachmentsToolbarButtonTrigger.tsx +24 -0
- package/src/components/BlockItemWrapper/Toolbar/AttachmentsToolbarButton/index.ts +3 -0
- package/src/components/BlockItemWrapper/Toolbar/BaseToolbarButton.spec.tsx +40 -0
- package/src/components/BlockItemWrapper/Toolbar/BaseToolbarButton.tsx +37 -0
- package/src/components/BlockItemWrapper/Toolbar/DragHandleToolbarButton/DragHandleToolbarButton.spec.tsx +89 -0
- package/src/components/BlockItemWrapper/Toolbar/DragHandleToolbarButton/DragHandleToolbarButton.tsx +40 -0
- package/src/components/BlockItemWrapper/Toolbar/DragHandleToolbarButton/index.ts +3 -0
- package/src/components/BlockItemWrapper/Toolbar/FlyoutToolbarButton/FlyoutToolbarButton.spec.tsx +140 -0
- package/src/components/BlockItemWrapper/Toolbar/FlyoutToolbarButton/FlyoutToolbarButton.tsx +61 -0
- package/src/components/BlockItemWrapper/Toolbar/FlyoutToolbarButton/index.ts +3 -0
- package/src/components/BlockItemWrapper/Toolbar/MenuToolbarButton/MenuToolbarButton.spec.tsx +77 -0
- package/src/components/BlockItemWrapper/Toolbar/MenuToolbarButton/MenuToolbarButton.tsx +30 -0
- package/src/components/BlockItemWrapper/Toolbar/MenuToolbarButton/ToolbarFlyoutMenu.spec.tsx +63 -0
- package/src/components/BlockItemWrapper/Toolbar/MenuToolbarButton/ToolbarFlyoutMenu.tsx +40 -0
- package/src/components/BlockItemWrapper/Toolbar/MenuToolbarButton/index.ts +3 -0
- package/src/components/BlockItemWrapper/Toolbar/Toolbar.spec.tsx +189 -57
- package/src/components/BlockItemWrapper/Toolbar/Toolbar.tsx +29 -126
- package/src/components/BlockItemWrapper/Toolbar/ToolbarButton/ToolbarButton.spec.tsx +70 -0
- package/src/components/BlockItemWrapper/Toolbar/ToolbarButton/ToolbarButton.tsx +19 -0
- package/src/components/BlockItemWrapper/Toolbar/ToolbarButton/index.ts +3 -0
- package/src/components/BlockItemWrapper/Toolbar/ToolbarButtonTooltip.tsx +25 -0
- package/src/components/BlockItemWrapper/Toolbar/context/DragPreviewContext.tsx +15 -0
- package/src/components/BlockItemWrapper/Toolbar/context/MultiFlyoutContext.tsx +25 -0
- package/src/components/BlockItemWrapper/Toolbar/context/index.ts +4 -0
- package/src/components/BlockItemWrapper/Toolbar/helpers.ts +1 -1
- package/src/components/BlockItemWrapper/Toolbar/hooks/index.ts +3 -0
- package/src/components/BlockItemWrapper/Toolbar/hooks/useMultiFlyoutState.spec.tsx +59 -0
- package/src/components/BlockItemWrapper/Toolbar/hooks/useMultiFlyoutState.ts +24 -0
- package/src/components/BlockItemWrapper/Toolbar/index.ts +6 -0
- package/src/components/BlockItemWrapper/Toolbar/types.ts +10 -30
- package/src/components/BlockItemWrapper/types.ts +1 -2
- package/src/helpers/mapColorPalettes.spec.ts +14 -113
- package/src/helpers/mapColorPalettes.ts +51 -8
- package/src/hooks/useAttachments.spec.tsx +1 -0
- package/tsconfig.json +1 -1
- package/dist/components/BlockItemWrapper/Toolbar/ToolbarAttachments.es.js +0 -27
- package/dist/components/BlockItemWrapper/Toolbar/ToolbarAttachments.es.js.map +0 -1
- package/dist/components/BlockItemWrapper/Toolbar/ToolbarAttachmentsTrigger.es.js +0 -12
- package/dist/components/BlockItemWrapper/Toolbar/ToolbarAttachmentsTrigger.es.js.map +0 -1
- package/src/components/BlockItemWrapper/Toolbar/ToolbarAttachments.tsx +0 -29
- package/src/components/BlockItemWrapper/Toolbar/ToolbarAttachmentsTrigger.tsx +0 -14
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/* (c) Copyright Frontify Ltd., all rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { useMemoizedId } from '@frontify/fondue';
|
|
4
|
+
|
|
5
|
+
import { useAttachmentsContext } from '../../../../hooks';
|
|
6
|
+
import { Attachments } from '../../../Attachments';
|
|
7
|
+
import { useMultiFlyoutState } from '../hooks/useMultiFlyoutState';
|
|
8
|
+
|
|
9
|
+
import { AttachmentsToolbarButtonTrigger } from './AttachmentsToolbarButtonTrigger';
|
|
10
|
+
import { useDragPreviewContext } from '../context/DragPreviewContext';
|
|
11
|
+
|
|
12
|
+
export const DEFAULT_ATTACHMENTS_BUTTON_ID = 'attachments';
|
|
13
|
+
|
|
14
|
+
type AttachmentsToolbarButtonProps = { flyoutId?: string };
|
|
15
|
+
|
|
16
|
+
export const AttachmentsToolbarButton = ({
|
|
17
|
+
flyoutId = DEFAULT_ATTACHMENTS_BUTTON_ID,
|
|
18
|
+
}: AttachmentsToolbarButtonProps) => {
|
|
19
|
+
const id = useMemoizedId(flyoutId);
|
|
20
|
+
|
|
21
|
+
const { appBridge, attachments, onAttachmentsAdd, onAttachmentDelete, onAttachmentReplace, onAttachmentsSorted } =
|
|
22
|
+
useAttachmentsContext();
|
|
23
|
+
|
|
24
|
+
const { isOpen, onOpenChange } = useMultiFlyoutState(id);
|
|
25
|
+
const isDragPreview = useDragPreviewContext();
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<Attachments
|
|
29
|
+
onUpload={onAttachmentsAdd}
|
|
30
|
+
onDelete={onAttachmentDelete}
|
|
31
|
+
onReplaceWithBrowse={onAttachmentReplace}
|
|
32
|
+
onReplaceWithUpload={onAttachmentReplace}
|
|
33
|
+
onSorted={onAttachmentsSorted}
|
|
34
|
+
onBrowse={onAttachmentsAdd}
|
|
35
|
+
items={attachments}
|
|
36
|
+
appBridge={appBridge}
|
|
37
|
+
triggerComponent={AttachmentsToolbarButtonTrigger}
|
|
38
|
+
isOpen={isOpen && !isDragPreview}
|
|
39
|
+
onOpenChange={onOpenChange}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/* (c) Copyright Frontify Ltd., all rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { cleanup, fireEvent, render } from '@testing-library/react';
|
|
4
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import { AttachmentsToolbarButtonTrigger } from './AttachmentsToolbarButtonTrigger';
|
|
6
|
+
import { MutableRefObject } from 'react';
|
|
7
|
+
|
|
8
|
+
const BUTTON_ID = 'attachments-toolbar-button-trigger';
|
|
9
|
+
|
|
10
|
+
describe('AttachmentsToolbarButtonTrigger', () => {
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
cleanup();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should apply active styles when flyout is open', async () => {
|
|
16
|
+
const { getByTestId } = render(
|
|
17
|
+
<AttachmentsToolbarButtonTrigger
|
|
18
|
+
isFlyoutOpen
|
|
19
|
+
triggerProps={{}}
|
|
20
|
+
triggerRef={{} as MutableRefObject<HTMLButtonElement>}
|
|
21
|
+
>
|
|
22
|
+
Button
|
|
23
|
+
</AttachmentsToolbarButtonTrigger>,
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
expect(getByTestId(BUTTON_ID)).toHaveClass('tw-text-box-neutral-inverse-pressed');
|
|
27
|
+
});
|
|
28
|
+
it('should forward trigger props to button', async () => {
|
|
29
|
+
const onPointerUpStub = vi.fn();
|
|
30
|
+
|
|
31
|
+
const { getByTestId } = render(
|
|
32
|
+
<AttachmentsToolbarButtonTrigger
|
|
33
|
+
isFlyoutOpen={false}
|
|
34
|
+
triggerProps={{ onPointerUp: onPointerUpStub }}
|
|
35
|
+
triggerRef={{} as MutableRefObject<HTMLButtonElement>}
|
|
36
|
+
>
|
|
37
|
+
Button
|
|
38
|
+
</AttachmentsToolbarButtonTrigger>,
|
|
39
|
+
);
|
|
40
|
+
await fireEvent.pointerUp(getByTestId(BUTTON_ID));
|
|
41
|
+
|
|
42
|
+
expect(onPointerUpStub).toHaveBeenCalledOnce();
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/* (c) Copyright Frontify Ltd., all rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { IconCaretDown12, IconPaperclip16 } from '@frontify/fondue';
|
|
4
|
+
import { type AttachmentsTriggerProps } from '../../../Attachments/types';
|
|
5
|
+
|
|
6
|
+
import { BaseToolbarButton } from '../BaseToolbarButton';
|
|
7
|
+
|
|
8
|
+
export const AttachmentsToolbarButtonTrigger = ({
|
|
9
|
+
children,
|
|
10
|
+
isFlyoutOpen,
|
|
11
|
+
triggerProps,
|
|
12
|
+
triggerRef,
|
|
13
|
+
}: AttachmentsTriggerProps) => (
|
|
14
|
+
<BaseToolbarButton
|
|
15
|
+
forceActiveStyle={isFlyoutOpen}
|
|
16
|
+
data-test-id="attachments-toolbar-button-trigger"
|
|
17
|
+
{...triggerProps}
|
|
18
|
+
ref={triggerRef}
|
|
19
|
+
>
|
|
20
|
+
<IconPaperclip16 />
|
|
21
|
+
{children}
|
|
22
|
+
<IconCaretDown12 />
|
|
23
|
+
</BaseToolbarButton>
|
|
24
|
+
);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/* (c) Copyright Frontify Ltd., all rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { cleanup, fireEvent, render } from '@testing-library/react';
|
|
4
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import { BaseToolbarButton } from './BaseToolbarButton';
|
|
6
|
+
|
|
7
|
+
const BUTTON_ID = 'base-toolbar-button';
|
|
8
|
+
|
|
9
|
+
describe('BaseToolbarButton', () => {
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
cleanup();
|
|
12
|
+
});
|
|
13
|
+
it('should call onClick', async () => {
|
|
14
|
+
const onClickStub = vi.fn();
|
|
15
|
+
const { getByTestId } = render(<BaseToolbarButton onClick={onClickStub}>Button</BaseToolbarButton>);
|
|
16
|
+
|
|
17
|
+
await fireEvent.click(getByTestId(BUTTON_ID));
|
|
18
|
+
|
|
19
|
+
expect(onClickStub).toHaveBeenCalledOnce();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should apply cursor styles', async () => {
|
|
23
|
+
const { getByTestId } = render(<BaseToolbarButton cursor="grab">Button</BaseToolbarButton>);
|
|
24
|
+
|
|
25
|
+
expect(getByTestId(BUTTON_ID)).toHaveClass('!tw-cursor-grab');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should apply active styles', async () => {
|
|
29
|
+
const { getByTestId } = render(<BaseToolbarButton forceActiveStyle>Button</BaseToolbarButton>);
|
|
30
|
+
|
|
31
|
+
expect(getByTestId(BUTTON_ID)).toHaveClass('tw-text-box-neutral-inverse-pressed');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should forward other attributes to button', async () => {
|
|
35
|
+
const BUTTON_TYPE = 'base';
|
|
36
|
+
const { getByTestId } = render(<BaseToolbarButton data-button-type={BUTTON_TYPE}>Button</BaseToolbarButton>);
|
|
37
|
+
|
|
38
|
+
expect(getByTestId(BUTTON_ID).dataset.buttonType).toEqual(BUTTON_TYPE);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/* (c) Copyright Frontify Ltd., all rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { type HTMLAttributes, type ReactNode, forwardRef } from 'react';
|
|
4
|
+
import { getToolbarButtonClassNames } from './helpers';
|
|
5
|
+
|
|
6
|
+
type BaseToolbarButtonProps = {
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
forceActiveStyle?: boolean;
|
|
9
|
+
cursor?: 'pointer' | 'grab';
|
|
10
|
+
'data-test-id'?: string;
|
|
11
|
+
} & HTMLAttributes<HTMLButtonElement>;
|
|
12
|
+
|
|
13
|
+
export const BaseToolbarButton = forwardRef<HTMLButtonElement, BaseToolbarButtonProps>(
|
|
14
|
+
(
|
|
15
|
+
{
|
|
16
|
+
onClick,
|
|
17
|
+
children,
|
|
18
|
+
forceActiveStyle,
|
|
19
|
+
cursor = 'pointer',
|
|
20
|
+
'data-test-id': dataTestId = 'base-toolbar-button',
|
|
21
|
+
...props
|
|
22
|
+
},
|
|
23
|
+
ref,
|
|
24
|
+
) => (
|
|
25
|
+
<button
|
|
26
|
+
onClick={onClick}
|
|
27
|
+
className={getToolbarButtonClassNames(cursor, forceActiveStyle)}
|
|
28
|
+
data-test-id={dataTestId}
|
|
29
|
+
{...props}
|
|
30
|
+
ref={ref}
|
|
31
|
+
>
|
|
32
|
+
{children}
|
|
33
|
+
</button>
|
|
34
|
+
),
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
BaseToolbarButton.displayName = 'BaseToolbarButton';
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/* (c) Copyright Frontify Ltd., all rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { fireEvent, render, waitFor } from '@testing-library/react';
|
|
4
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import { DragHandleToolbarButton } from './DragHandleToolbarButton';
|
|
6
|
+
import { IconAdobeCreativeCloud } from '@frontify/fondue';
|
|
7
|
+
import { DragPreviewContextProvider } from '../context/DragPreviewContext';
|
|
8
|
+
|
|
9
|
+
const TOOLBAR_BUTTON_ID = 'block-item-wrapper-toolbar-btn';
|
|
10
|
+
const TOOLTIP_ID = 'toolbar-button-tooltip';
|
|
11
|
+
|
|
12
|
+
const TOOLTIP_CONTENT = 'content';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @vitest-environment happy-dom
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
describe('DragHandleToolbarButton', () => {
|
|
19
|
+
it('should show tooltip and activeStyles when item is in drag preview context', async () => {
|
|
20
|
+
const { getByTestId } = render(
|
|
21
|
+
<DragPreviewContextProvider isDragPreview>
|
|
22
|
+
<DragHandleToolbarButton
|
|
23
|
+
tooltip={TOOLTIP_CONTENT}
|
|
24
|
+
icon={<IconAdobeCreativeCloud />}
|
|
25
|
+
draggableProps={{}}
|
|
26
|
+
/>
|
|
27
|
+
</DragPreviewContextProvider>,
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
expect(getByTestId(TOOLTIP_ID)).not.toHaveClass('tw-opacity-0');
|
|
31
|
+
expect(getByTestId(TOOLBAR_BUTTON_ID)).toHaveClass('tw-cursor-grabbing');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should show tooltip when item is focused', async () => {
|
|
35
|
+
const { getByTestId } = render(
|
|
36
|
+
<DragHandleToolbarButton tooltip={TOOLTIP_CONTENT} icon={<IconAdobeCreativeCloud />} draggableProps={{}} />,
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
expect(getByTestId(TOOLTIP_ID)).toHaveClass('tw-opacity-0');
|
|
40
|
+
expect(getByTestId(TOOLTIP_ID)).toHaveTextContent(TOOLTIP_CONTENT);
|
|
41
|
+
|
|
42
|
+
getByTestId(TOOLBAR_BUTTON_ID).focus();
|
|
43
|
+
|
|
44
|
+
await waitFor(() => {
|
|
45
|
+
expect(getByTestId(TOOLTIP_ID)).not.toHaveClass('tw-opacity-0');
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should forward draggableProps', async () => {
|
|
50
|
+
const onDragStub = vi.fn();
|
|
51
|
+
|
|
52
|
+
const { getByTestId } = render(
|
|
53
|
+
<DragHandleToolbarButton
|
|
54
|
+
tooltip={TOOLTIP_CONTENT}
|
|
55
|
+
icon={<IconAdobeCreativeCloud />}
|
|
56
|
+
draggableProps={{ onDrag: onDragStub }}
|
|
57
|
+
/>,
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
await fireEvent.drag(getByTestId(TOOLBAR_BUTTON_ID));
|
|
61
|
+
|
|
62
|
+
expect(onDragStub).toHaveBeenCalledOnce();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should forward setActivatorNodeRef', async () => {
|
|
66
|
+
const setActivatorNodeRefStub = vi.fn();
|
|
67
|
+
|
|
68
|
+
render(
|
|
69
|
+
<DragHandleToolbarButton
|
|
70
|
+
tooltip={TOOLTIP_CONTENT}
|
|
71
|
+
icon={<IconAdobeCreativeCloud />}
|
|
72
|
+
draggableProps={{}}
|
|
73
|
+
setActivatorNodeRef={setActivatorNodeRefStub}
|
|
74
|
+
/>,
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
expect(setActivatorNodeRefStub).toHaveBeenCalledOnce();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should display icon', async () => {
|
|
81
|
+
const { getByTestId } = render(
|
|
82
|
+
<DragHandleToolbarButton tooltip={TOOLTIP_CONTENT} icon={<IconAdobeCreativeCloud />} draggableProps={{}} />,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const icons = [...getByTestId(TOOLBAR_BUTTON_ID).querySelectorAll('svg')];
|
|
86
|
+
expect(icons).toHaveLength(1);
|
|
87
|
+
expect(icons[0].outerHTML).toMatch('IconAdobeCreativeCloud');
|
|
88
|
+
});
|
|
89
|
+
});
|
package/src/components/BlockItemWrapper/Toolbar/DragHandleToolbarButton/DragHandleToolbarButton.tsx
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/* (c) Copyright Frontify Ltd., all rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { ReactNode } from 'react';
|
|
4
|
+
import { DEFAULT_DRAGGING_TOOLTIP, DEFAULT_DRAG_TOOLTIP } from '../../constants';
|
|
5
|
+
import { useDragPreviewContext } from '../context/DragPreviewContext';
|
|
6
|
+
import { BaseToolbarButton } from '../BaseToolbarButton';
|
|
7
|
+
import { ToolbarButtonTooltip } from '../ToolbarButtonTooltip';
|
|
8
|
+
|
|
9
|
+
export type DragHandleToolbarButtonProps = {
|
|
10
|
+
icon?: ReactNode;
|
|
11
|
+
tooltip?: string;
|
|
12
|
+
draggableProps: Record<string, unknown>;
|
|
13
|
+
setActivatorNodeRef?: (node: HTMLElement | null) => void;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const DragHandleToolbarButton = ({
|
|
17
|
+
tooltip,
|
|
18
|
+
icon,
|
|
19
|
+
setActivatorNodeRef,
|
|
20
|
+
draggableProps,
|
|
21
|
+
}: DragHandleToolbarButtonProps) => {
|
|
22
|
+
const isDragPreview = useDragPreviewContext();
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<ToolbarButtonTooltip
|
|
26
|
+
open={isDragPreview}
|
|
27
|
+
content={<div>{isDragPreview ? DEFAULT_DRAGGING_TOOLTIP : tooltip ?? DEFAULT_DRAG_TOOLTIP}</div>}
|
|
28
|
+
>
|
|
29
|
+
<BaseToolbarButton
|
|
30
|
+
ref={setActivatorNodeRef}
|
|
31
|
+
data-test-id="block-item-wrapper-toolbar-btn"
|
|
32
|
+
forceActiveStyle={isDragPreview}
|
|
33
|
+
cursor="grab"
|
|
34
|
+
{...draggableProps}
|
|
35
|
+
>
|
|
36
|
+
{icon}
|
|
37
|
+
</BaseToolbarButton>
|
|
38
|
+
</ToolbarButtonTooltip>
|
|
39
|
+
);
|
|
40
|
+
};
|
package/src/components/BlockItemWrapper/Toolbar/FlyoutToolbarButton/FlyoutToolbarButton.spec.tsx
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/* (c) Copyright Frontify Ltd., all rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { fireEvent, render, waitFor } from '@testing-library/react';
|
|
4
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import { FlyoutToolbarButton } from './FlyoutToolbarButton';
|
|
6
|
+
import { MultiFlyoutContextProvider } from '../context/MultiFlyoutContext';
|
|
7
|
+
import { IconAdobeCreativeCloud } from '@frontify/fondue';
|
|
8
|
+
import { DragPreviewContextProvider } from '../context/DragPreviewContext';
|
|
9
|
+
|
|
10
|
+
const BUTTON_ID = 'block-item-wrapper-toolbar-flyout';
|
|
11
|
+
const TOOLTIP_ID = 'toolbar-button-tooltip';
|
|
12
|
+
|
|
13
|
+
const TEST_FLYOUT_ID = 'test';
|
|
14
|
+
const TEST_TOOLTIP = 'tooltip';
|
|
15
|
+
/**
|
|
16
|
+
* @vitest-environment happy-dom
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
describe('FlyoutToolbarButton', () => {
|
|
20
|
+
it('should log error if not inside a flyout provider when opening', async () => {
|
|
21
|
+
vi.spyOn(console, 'error');
|
|
22
|
+
const { getByTestId } = render(
|
|
23
|
+
<FlyoutToolbarButton
|
|
24
|
+
flyoutId={TEST_FLYOUT_ID}
|
|
25
|
+
icon={<IconAdobeCreativeCloud />}
|
|
26
|
+
tooltip={TEST_TOOLTIP}
|
|
27
|
+
content="screen"
|
|
28
|
+
/>,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
await fireEvent.click(getByTestId(BUTTON_ID));
|
|
32
|
+
|
|
33
|
+
expect(console.error).toBeCalled();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should use flyoutId in flyout context', async () => {
|
|
37
|
+
const setOpenFlyoutIdsStub = vi.fn();
|
|
38
|
+
|
|
39
|
+
const { getByTestId } = render(
|
|
40
|
+
<MultiFlyoutContextProvider openFlyoutIds={[]} setOpenFlyoutIds={setOpenFlyoutIdsStub}>
|
|
41
|
+
<FlyoutToolbarButton
|
|
42
|
+
flyoutId={TEST_FLYOUT_ID}
|
|
43
|
+
icon={<IconAdobeCreativeCloud />}
|
|
44
|
+
tooltip={TEST_TOOLTIP}
|
|
45
|
+
content="children"
|
|
46
|
+
/>
|
|
47
|
+
</MultiFlyoutContextProvider>,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
await fireEvent.click(getByTestId(BUTTON_ID));
|
|
51
|
+
|
|
52
|
+
expect(setOpenFlyoutIdsStub).toHaveBeenCalled();
|
|
53
|
+
const dispatchedStateResult = setOpenFlyoutIdsStub.mock.lastCall[0]([]);
|
|
54
|
+
expect(dispatchedStateResult).toEqual([TEST_FLYOUT_ID]);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should display content', async () => {
|
|
58
|
+
const setOpenFlyoutIdsStub = vi.fn();
|
|
59
|
+
|
|
60
|
+
const { getByTestId } = render(
|
|
61
|
+
<MultiFlyoutContextProvider openFlyoutIds={[TEST_FLYOUT_ID]} setOpenFlyoutIds={setOpenFlyoutIdsStub}>
|
|
62
|
+
<FlyoutToolbarButton
|
|
63
|
+
flyoutId={TEST_FLYOUT_ID}
|
|
64
|
+
icon={<IconAdobeCreativeCloud />}
|
|
65
|
+
tooltip={TEST_TOOLTIP}
|
|
66
|
+
flyoutFooter={<div data-test-id="footer">Footer</div>}
|
|
67
|
+
flyoutHeader={<div data-test-id="header">Header</div>}
|
|
68
|
+
content={<div data-test-id="content">Content</div>}
|
|
69
|
+
/>
|
|
70
|
+
</MultiFlyoutContextProvider>,
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
expect(getByTestId('content')).toBeVisible();
|
|
74
|
+
expect(getByTestId('header')).toBeVisible();
|
|
75
|
+
expect(getByTestId('footer')).toBeVisible();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should show tooltip content', async () => {
|
|
79
|
+
const setOpenFlyoutIdsStub = vi.fn();
|
|
80
|
+
|
|
81
|
+
const { getByTestId } = render(
|
|
82
|
+
<MultiFlyoutContextProvider openFlyoutIds={[]} setOpenFlyoutIds={setOpenFlyoutIdsStub}>
|
|
83
|
+
<FlyoutToolbarButton
|
|
84
|
+
flyoutId={TEST_FLYOUT_ID}
|
|
85
|
+
icon={<IconAdobeCreativeCloud />}
|
|
86
|
+
tooltip={TEST_TOOLTIP}
|
|
87
|
+
content="children"
|
|
88
|
+
/>
|
|
89
|
+
</MultiFlyoutContextProvider>,
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
getByTestId(BUTTON_ID).focus();
|
|
93
|
+
|
|
94
|
+
await waitFor(() => expect(getByTestId(TOOLTIP_ID)).not.toHaveClass('tw-opacity-0'));
|
|
95
|
+
expect(getByTestId(TOOLTIP_ID)).toHaveTextContent(TEST_TOOLTIP);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should use supplied icon', async () => {
|
|
99
|
+
const setOpenFlyoutIdsStub = vi.fn();
|
|
100
|
+
|
|
101
|
+
const { getByTestId } = render(
|
|
102
|
+
<MultiFlyoutContextProvider openFlyoutIds={[]} setOpenFlyoutIds={setOpenFlyoutIdsStub}>
|
|
103
|
+
<FlyoutToolbarButton
|
|
104
|
+
flyoutId={TEST_FLYOUT_ID}
|
|
105
|
+
icon={<IconAdobeCreativeCloud />}
|
|
106
|
+
tooltip={TEST_TOOLTIP}
|
|
107
|
+
content="content"
|
|
108
|
+
/>
|
|
109
|
+
</MultiFlyoutContextProvider>,
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const icons = [...getByTestId(BUTTON_ID).querySelectorAll('svg')];
|
|
113
|
+
expect(icons).toHaveLength(1);
|
|
114
|
+
expect(icons[0].outerHTML).toMatch('IconAdobeCreativeCloud');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should disable tooltip and flyout when content is inside drag preview', async () => {
|
|
118
|
+
const { getByTestId, queryByTestId } = render(
|
|
119
|
+
<MultiFlyoutContextProvider openFlyoutIds={[TEST_FLYOUT_ID]} setOpenFlyoutIds={vi.fn()}>
|
|
120
|
+
<DragPreviewContextProvider isDragPreview>
|
|
121
|
+
<FlyoutToolbarButton
|
|
122
|
+
flyoutId={TEST_FLYOUT_ID}
|
|
123
|
+
icon={<IconAdobeCreativeCloud />}
|
|
124
|
+
tooltip={TEST_TOOLTIP}
|
|
125
|
+
content={<div data-test-id="content">Content</div>}
|
|
126
|
+
/>
|
|
127
|
+
</DragPreviewContextProvider>
|
|
128
|
+
</MultiFlyoutContextProvider>,
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
expect(getByTestId(TOOLTIP_ID)).toHaveClass('tw-opacity-0');
|
|
132
|
+
|
|
133
|
+
getByTestId(TOOLTIP_ID).focus();
|
|
134
|
+
|
|
135
|
+
await waitFor(() => {
|
|
136
|
+
expect(getByTestId(TOOLTIP_ID)).toHaveClass('tw-opacity-0');
|
|
137
|
+
expect(queryByTestId('content')).toBeNull();
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/* (c) Copyright Frontify Ltd., all rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { type MutableRefObject, type ReactNode } from 'react';
|
|
4
|
+
import { useDragPreviewContext } from '../context/DragPreviewContext';
|
|
5
|
+
import { ToolbarButtonTooltip } from '../ToolbarButtonTooltip';
|
|
6
|
+
import { Flyout, FlyoutPlacement, useMemoizedId } from '@frontify/fondue';
|
|
7
|
+
import { BaseToolbarButton } from '../BaseToolbarButton';
|
|
8
|
+
import { useMultiFlyoutState } from '../hooks/useMultiFlyoutState';
|
|
9
|
+
|
|
10
|
+
export type FlyoutToolbarButtonProps = {
|
|
11
|
+
content: ReactNode;
|
|
12
|
+
icon: ReactNode;
|
|
13
|
+
tooltip: ReactNode;
|
|
14
|
+
flyoutId: string;
|
|
15
|
+
flyoutFooter?: ReactNode;
|
|
16
|
+
flyoutHeader?: ReactNode;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const FlyoutToolbarButton = ({
|
|
20
|
+
content,
|
|
21
|
+
icon,
|
|
22
|
+
tooltip,
|
|
23
|
+
flyoutId,
|
|
24
|
+
flyoutFooter,
|
|
25
|
+
flyoutHeader,
|
|
26
|
+
}: FlyoutToolbarButtonProps) => {
|
|
27
|
+
const id = useMemoizedId(flyoutId);
|
|
28
|
+
|
|
29
|
+
const { isOpen, onOpenChange } = useMultiFlyoutState(id);
|
|
30
|
+
|
|
31
|
+
const isDragPreview = useDragPreviewContext();
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<ToolbarButtonTooltip disabled={isDragPreview || isOpen} content={tooltip}>
|
|
35
|
+
<div className="tw-flex tw-flex-shrink-0 tw-flex-1 tw-h-6 tw-relative">
|
|
36
|
+
<Flyout
|
|
37
|
+
isOpen={isOpen && !isDragPreview}
|
|
38
|
+
legacyFooter={false}
|
|
39
|
+
fixedFooter={flyoutFooter}
|
|
40
|
+
fixedHeader={flyoutHeader}
|
|
41
|
+
fitContent
|
|
42
|
+
hug={false}
|
|
43
|
+
placement={FlyoutPlacement.BottomRight}
|
|
44
|
+
onOpenChange={onOpenChange}
|
|
45
|
+
trigger={(triggerProps, triggerRef) => (
|
|
46
|
+
<BaseToolbarButton
|
|
47
|
+
data-test-id="block-item-wrapper-toolbar-flyout"
|
|
48
|
+
forceActiveStyle={isOpen && !isDragPreview}
|
|
49
|
+
{...triggerProps}
|
|
50
|
+
ref={triggerRef as MutableRefObject<HTMLButtonElement>}
|
|
51
|
+
>
|
|
52
|
+
{icon}
|
|
53
|
+
</BaseToolbarButton>
|
|
54
|
+
)}
|
|
55
|
+
>
|
|
56
|
+
{content}
|
|
57
|
+
</Flyout>
|
|
58
|
+
</div>
|
|
59
|
+
</ToolbarButtonTooltip>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/* (c) Copyright Frontify Ltd., all rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { fireEvent, render, waitFor } from '@testing-library/react';
|
|
4
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import { IconAdobeCreativeCloud } from '@frontify/fondue';
|
|
6
|
+
import { MenuToolbarButton } from '.';
|
|
7
|
+
import { MultiFlyoutContextProvider } from '../context/MultiFlyoutContext';
|
|
8
|
+
|
|
9
|
+
const BUTTON_ID = 'block-item-wrapper-toolbar-flyout';
|
|
10
|
+
const MENU_ITEM_ID = 'menu-item';
|
|
11
|
+
const TOOLTIP_ID = 'toolbar-button-tooltip';
|
|
12
|
+
|
|
13
|
+
const TEST_FLYOUT_ID = 'test';
|
|
14
|
+
const TEST_TOOLTIP = 'tooltip';
|
|
15
|
+
/**
|
|
16
|
+
* @vitest-environment happy-dom
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
describe('MenuToolbarButton', () => {
|
|
20
|
+
it('should log error if not inside a flyout provider when opening', async () => {
|
|
21
|
+
vi.spyOn(console, 'error');
|
|
22
|
+
const { getByTestId } = render(<MenuToolbarButton items={[]} />);
|
|
23
|
+
|
|
24
|
+
await fireEvent.click(getByTestId(BUTTON_ID));
|
|
25
|
+
|
|
26
|
+
expect(console.error).toBeCalled();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should use flyout Id in flyout context', async () => {
|
|
30
|
+
const setOpenFlyoutIdsStub = vi.fn();
|
|
31
|
+
|
|
32
|
+
const { getByTestId } = render(
|
|
33
|
+
<MultiFlyoutContextProvider openFlyoutIds={[]} setOpenFlyoutIds={setOpenFlyoutIdsStub}>
|
|
34
|
+
<MenuToolbarButton items={[]} flyoutId={TEST_FLYOUT_ID} />
|
|
35
|
+
</MultiFlyoutContextProvider>,
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
await fireEvent.click(getByTestId(BUTTON_ID));
|
|
39
|
+
|
|
40
|
+
expect(setOpenFlyoutIdsStub).toHaveBeenCalled();
|
|
41
|
+
const dispatchedStateResult = setOpenFlyoutIdsStub.mock.lastCall[0]([]);
|
|
42
|
+
expect(dispatchedStateResult).toEqual([TEST_FLYOUT_ID]);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should display menu items', async () => {
|
|
46
|
+
const setOpenFlyoutIdsStub = vi.fn();
|
|
47
|
+
|
|
48
|
+
const { getAllByTestId } = render(
|
|
49
|
+
<MultiFlyoutContextProvider openFlyoutIds={[TEST_FLYOUT_ID]} setOpenFlyoutIds={setOpenFlyoutIdsStub}>
|
|
50
|
+
<MenuToolbarButton
|
|
51
|
+
items={[
|
|
52
|
+
[{ title: 'item-1', onClick: vi.fn(), icon: <IconAdobeCreativeCloud /> }],
|
|
53
|
+
[{ title: 'item-2', onClick: vi.fn(), icon: <IconAdobeCreativeCloud /> }],
|
|
54
|
+
]}
|
|
55
|
+
flyoutId={TEST_FLYOUT_ID}
|
|
56
|
+
/>
|
|
57
|
+
</MultiFlyoutContextProvider>,
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
expect(getAllByTestId(MENU_ITEM_ID)).toHaveLength(2);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should show tooltip content', async () => {
|
|
64
|
+
const setOpenFlyoutIdsStub = vi.fn();
|
|
65
|
+
|
|
66
|
+
const { getByTestId } = render(
|
|
67
|
+
<MultiFlyoutContextProvider openFlyoutIds={[]} setOpenFlyoutIds={setOpenFlyoutIdsStub}>
|
|
68
|
+
<MenuToolbarButton items={[]} tooltip={TEST_TOOLTIP} flyoutId={TEST_FLYOUT_ID} />
|
|
69
|
+
</MultiFlyoutContextProvider>,
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
getByTestId(BUTTON_ID).focus();
|
|
73
|
+
|
|
74
|
+
await waitFor(() => expect(getByTestId(TOOLTIP_ID)).not.toHaveClass('tw-opacity-0'));
|
|
75
|
+
expect(getByTestId(TOOLTIP_ID)).toHaveTextContent(TEST_TOOLTIP);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/* (c) Copyright Frontify Ltd., all rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { IconDotsHorizontal16, useMemoizedId } from '@frontify/fondue';
|
|
4
|
+
import { ToolbarFlyoutMenu, ToolbarFlyoutMenuItem } from './ToolbarFlyoutMenu';
|
|
5
|
+
import { FlyoutToolbarButton } from '../FlyoutToolbarButton/FlyoutToolbarButton';
|
|
6
|
+
|
|
7
|
+
export const DEFAULT_MENU_BUTTON_ID = 'menu';
|
|
8
|
+
|
|
9
|
+
export type MenuToolbarButtonProps = {
|
|
10
|
+
items: ToolbarFlyoutMenuItem[][];
|
|
11
|
+
flyoutId?: string;
|
|
12
|
+
tooltip?: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const MenuToolbarButton = ({
|
|
16
|
+
items,
|
|
17
|
+
flyoutId = DEFAULT_MENU_BUTTON_ID,
|
|
18
|
+
tooltip = 'Options',
|
|
19
|
+
}: MenuToolbarButtonProps) => {
|
|
20
|
+
const id = useMemoizedId(flyoutId);
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<FlyoutToolbarButton
|
|
24
|
+
icon={<IconDotsHorizontal16 />}
|
|
25
|
+
tooltip={tooltip}
|
|
26
|
+
flyoutId={id}
|
|
27
|
+
content={<ToolbarFlyoutMenu items={items} flyoutId={id} />}
|
|
28
|
+
/>
|
|
29
|
+
);
|
|
30
|
+
};
|