@axinom/mosaic-ui 0.75.0-rc.5 → 0.77.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/dist/THIRD-PARTY-LICENSES.txt +1296 -0
- package/dist/cjs/components/ActionBar/ActionBar.d.ts +26 -0
- package/dist/cjs/components/ActionBar/ActionBar.d.ts.map +1 -0
- package/dist/cjs/components/ActionBar/ActionBar.model.d.ts +49 -0
- package/dist/cjs/components/ActionBar/ActionBar.model.d.ts.map +1 -0
- package/dist/cjs/components/ActionBar/ActionBarButton/ActionBarButton.d.ts +22 -0
- package/dist/cjs/components/ActionBar/ActionBarButton/ActionBarButton.d.ts.map +1 -0
- package/dist/cjs/components/ActionBar/helpers.d.ts +22 -0
- package/dist/cjs/components/ActionBar/helpers.d.ts.map +1 -0
- package/dist/cjs/components/ActionBar/index.d.ts +3 -0
- package/dist/cjs/components/ActionBar/index.d.ts.map +1 -0
- package/dist/cjs/components/Actions/Action/Action.d.ts.map +1 -1
- package/dist/cjs/components/ConfirmDialog/index.d.ts +1 -0
- package/dist/cjs/components/ConfirmDialog/index.d.ts.map +1 -1
- package/dist/cjs/components/ConfirmDialog/useConfirmableAction.d.ts +55 -0
- package/dist/cjs/components/ConfirmDialog/useConfirmableAction.d.ts.map +1 -0
- package/dist/cjs/components/Explorer/BulkEdit/useBulkEdit.d.ts.map +1 -1
- package/dist/cjs/components/Explorer/Explorer.d.ts.map +1 -1
- package/dist/cjs/components/Explorer/QuickEdit/useQuickEdit.d.ts +14 -3
- package/dist/cjs/components/Explorer/QuickEdit/useQuickEdit.d.ts.map +1 -1
- package/dist/cjs/components/Explorer/helpers/useActions.d.ts +15 -4
- package/dist/cjs/components/Explorer/helpers/useActions.d.ts.map +1 -1
- package/dist/cjs/components/Explorer/helpers/useSubtitle.d.ts.map +1 -1
- package/dist/cjs/components/List/List.d.ts +17 -3
- package/dist/cjs/components/List/List.d.ts.map +1 -1
- package/dist/cjs/components/List/List.model.d.ts +5 -1
- package/dist/cjs/components/List/List.model.d.ts.map +1 -1
- package/dist/cjs/components/List/ListHeader/ListHeader.d.ts +10 -1
- package/dist/cjs/components/List/ListHeader/ListHeader.d.ts.map +1 -1
- package/dist/cjs/components/List/ListRow/ListRow.d.ts +16 -5
- package/dist/cjs/components/List/ListRow/ListRow.d.ts.map +1 -1
- package/dist/cjs/components/List/ListRowRenderer/ListRowRenderer.d.ts.map +1 -1
- package/dist/cjs/components/List/helpers.d.ts +11 -3
- package/dist/cjs/components/List/helpers.d.ts.map +1 -1
- package/dist/cjs/components/List/useColumnsSize.d.ts +4 -2
- package/dist/cjs/components/List/useColumnsSize.d.ts.map +1 -1
- package/dist/cjs/components/PageHeader/PageHeader.d.ts +4 -1
- package/dist/cjs/components/PageHeader/PageHeader.d.ts.map +1 -1
- package/dist/cjs/components/PageHeader/PageHeader.model.d.ts +37 -1
- package/dist/cjs/components/PageHeader/PageHeader.model.d.ts.map +1 -1
- package/dist/cjs/components/PageHeader/PageHeaderAction/PageHeaderAction.d.ts +3 -0
- package/dist/cjs/components/PageHeader/PageHeaderAction/PageHeaderAction.d.ts.map +1 -1
- package/dist/cjs/components/PageHeader/PageHeaderAction/PageHeaderAction.model.d.ts +8 -0
- package/dist/cjs/components/PageHeader/PageHeaderAction/PageHeaderAction.model.d.ts.map +1 -1
- package/dist/cjs/components/PageHeader/helpers.d.ts +9 -0
- package/dist/cjs/components/PageHeader/helpers.d.ts.map +1 -0
- package/dist/cjs/components/index.d.ts +1 -0
- package/dist/cjs/components/index.d.ts.map +1 -1
- package/dist/cjs/hooks/index.d.ts +1 -0
- package/dist/cjs/hooks/index.d.ts.map +1 -1
- package/dist/cjs/hooks/useElementWidthObserver/useElementWidthObserver.d.ts.map +1 -0
- package/dist/cjs/index.js +4 -4
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/components/ActionBar/ActionBar.d.ts +26 -0
- package/dist/esm/components/ActionBar/ActionBar.d.ts.map +1 -0
- package/dist/esm/components/ActionBar/ActionBar.model.d.ts +49 -0
- package/dist/esm/components/ActionBar/ActionBar.model.d.ts.map +1 -0
- package/dist/esm/components/ActionBar/ActionBarButton/ActionBarButton.d.ts +22 -0
- package/dist/esm/components/ActionBar/ActionBarButton/ActionBarButton.d.ts.map +1 -0
- package/dist/esm/components/ActionBar/helpers.d.ts +22 -0
- package/dist/esm/components/ActionBar/helpers.d.ts.map +1 -0
- package/dist/esm/components/ActionBar/index.d.ts +3 -0
- package/dist/esm/components/ActionBar/index.d.ts.map +1 -0
- package/dist/esm/components/Actions/Action/Action.d.ts.map +1 -1
- package/dist/esm/components/ConfirmDialog/index.d.ts +1 -0
- package/dist/esm/components/ConfirmDialog/index.d.ts.map +1 -1
- package/dist/esm/components/ConfirmDialog/useConfirmableAction.d.ts +55 -0
- package/dist/esm/components/ConfirmDialog/useConfirmableAction.d.ts.map +1 -0
- package/dist/esm/components/Explorer/BulkEdit/useBulkEdit.d.ts.map +1 -1
- package/dist/esm/components/Explorer/Explorer.d.ts.map +1 -1
- package/dist/esm/components/Explorer/QuickEdit/useQuickEdit.d.ts +14 -3
- package/dist/esm/components/Explorer/QuickEdit/useQuickEdit.d.ts.map +1 -1
- package/dist/esm/components/Explorer/helpers/useActions.d.ts +15 -4
- package/dist/esm/components/Explorer/helpers/useActions.d.ts.map +1 -1
- package/dist/esm/components/Explorer/helpers/useSubtitle.d.ts.map +1 -1
- package/dist/esm/components/List/List.d.ts +17 -3
- package/dist/esm/components/List/List.d.ts.map +1 -1
- package/dist/esm/components/List/List.model.d.ts +5 -1
- package/dist/esm/components/List/List.model.d.ts.map +1 -1
- package/dist/esm/components/List/ListHeader/ListHeader.d.ts +10 -1
- package/dist/esm/components/List/ListHeader/ListHeader.d.ts.map +1 -1
- package/dist/esm/components/List/ListRow/ListRow.d.ts +16 -5
- package/dist/esm/components/List/ListRow/ListRow.d.ts.map +1 -1
- package/dist/esm/components/List/ListRowRenderer/ListRowRenderer.d.ts.map +1 -1
- package/dist/esm/components/List/helpers.d.ts +11 -3
- package/dist/esm/components/List/helpers.d.ts.map +1 -1
- package/dist/esm/components/List/useColumnsSize.d.ts +4 -2
- package/dist/esm/components/List/useColumnsSize.d.ts.map +1 -1
- package/dist/esm/components/PageHeader/PageHeader.d.ts +4 -1
- package/dist/esm/components/PageHeader/PageHeader.d.ts.map +1 -1
- package/dist/esm/components/PageHeader/PageHeader.model.d.ts +37 -1
- package/dist/esm/components/PageHeader/PageHeader.model.d.ts.map +1 -1
- package/dist/esm/components/PageHeader/PageHeaderAction/PageHeaderAction.d.ts +3 -0
- package/dist/esm/components/PageHeader/PageHeaderAction/PageHeaderAction.d.ts.map +1 -1
- package/dist/esm/components/PageHeader/PageHeaderAction/PageHeaderAction.model.d.ts +8 -0
- package/dist/esm/components/PageHeader/PageHeaderAction/PageHeaderAction.model.d.ts.map +1 -1
- package/dist/esm/components/PageHeader/helpers.d.ts +9 -0
- package/dist/esm/components/PageHeader/helpers.d.ts.map +1 -0
- package/dist/esm/components/index.d.ts +1 -0
- package/dist/esm/components/index.d.ts.map +1 -1
- package/dist/esm/hooks/index.d.ts +1 -0
- package/dist/esm/hooks/index.d.ts.map +1 -1
- package/dist/esm/hooks/useElementWidthObserver/useElementWidthObserver.d.ts.map +1 -0
- package/dist/esm/index.js +4 -4
- package/dist/esm/index.js.map +1 -1
- package/package.json +7 -3
- package/src/components/ActionBar/ActionBar.model.ts +61 -0
- package/src/components/ActionBar/ActionBar.module.scss +64 -0
- package/src/components/ActionBar/ActionBar.spec.tsx +398 -0
- package/src/components/ActionBar/ActionBar.stories.tsx +205 -0
- package/src/components/ActionBar/ActionBar.tsx +170 -0
- package/src/components/ActionBar/ActionBarButton/ActionBarButton.module.scss +140 -0
- package/src/components/ActionBar/ActionBarButton/ActionBarButton.tsx +193 -0
- package/src/components/ActionBar/helpers.spec.ts +41 -0
- package/src/components/ActionBar/helpers.ts +54 -0
- package/src/components/ActionBar/index.ts +2 -0
- package/src/components/Actions/Action/Action.spec.tsx +64 -2
- package/src/components/Actions/Action/Action.tsx +6 -1
- package/src/components/ConfirmDialog/index.ts +1 -0
- package/src/components/ConfirmDialog/useConfirmableAction.tsx +159 -0
- package/src/components/EmptyStation/EmptyStation.stories.tsx +97 -0
- package/src/components/Explorer/BulkEdit/useBulkEdit.tsx +2 -0
- package/src/components/Explorer/Explorer.spec.tsx +97 -8
- package/src/components/Explorer/Explorer.tsx +26 -13
- package/src/components/Explorer/NavigationExplorer/NavigationExplorer.stories.tsx +124 -1
- package/src/components/Explorer/QuickEdit/useQuickEdit.spec.tsx +94 -50
- package/src/components/Explorer/QuickEdit/useQuickEdit.tsx +101 -42
- package/src/components/Explorer/SelectionExplorer/SelectionExplorer.spec.tsx +46 -13
- package/src/components/Explorer/helpers/useActions.ts +125 -73
- package/src/components/Explorer/helpers/useSubtitle.spec.tsx +33 -23
- package/src/components/Explorer/helpers/useSubtitle.tsx +18 -26
- package/src/components/List/List.model.ts +5 -1
- package/src/components/List/List.module.scss +25 -6
- package/src/components/List/List.spec.tsx +498 -1
- package/src/components/List/List.stories.tsx +137 -8
- package/src/components/List/List.tsx +305 -194
- package/src/components/List/ListHeader/ListHeader.module.scss +14 -0
- package/src/components/List/ListHeader/ListHeader.spec.tsx +1 -0
- package/src/components/List/ListHeader/ListHeader.tsx +25 -8
- package/src/components/List/ListRow/ListRow.module.scss +1 -1
- package/src/components/List/ListRow/ListRow.spec.tsx +109 -22
- package/src/components/List/ListRow/ListRow.tsx +52 -13
- package/src/components/List/ListRowRenderer/ListRowRenderer.tsx +28 -22
- package/src/components/List/helpers.spec.ts +29 -0
- package/src/components/List/helpers.ts +17 -9
- package/src/components/List/useColumnsSize.ts +17 -7
- package/src/components/PageHeader/PageHeader.model.ts +38 -1
- package/src/components/PageHeader/PageHeader.module.scss +39 -18
- package/src/components/PageHeader/PageHeader.spec.tsx +407 -18
- package/src/components/PageHeader/PageHeader.stories.tsx +95 -12
- package/src/components/PageHeader/PageHeader.tsx +89 -38
- package/src/components/PageHeader/PageHeaderAction/PageHeaderAction.model.ts +8 -0
- package/src/components/PageHeader/PageHeaderAction/PageHeaderAction.module.scss +50 -42
- package/src/components/PageHeader/PageHeaderAction/PageHeaderAction.spec.tsx +193 -1
- package/src/components/PageHeader/PageHeaderAction/PageHeaderAction.stories.tsx +36 -0
- package/src/components/PageHeader/PageHeaderAction/PageHeaderAction.tsx +81 -130
- package/src/components/PageHeader/helpers.spec.ts +60 -0
- package/src/components/PageHeader/helpers.ts +36 -0
- package/src/components/index.ts +1 -0
- package/src/hooks/index.ts +1 -0
- package/src/styles/variables.scss +41 -7
- package/dist/cjs/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroup.d.ts +0 -35
- package/dist/cjs/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroup.d.ts.map +0 -1
- package/dist/cjs/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContext.d.ts +0 -6
- package/dist/cjs/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContext.d.ts.map +0 -1
- package/dist/cjs/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContextProvider.d.ts +0 -3
- package/dist/cjs/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContextProvider.d.ts.map +0 -1
- package/dist/cjs/components/PageHeader/helpers/useElementWidthObserver.d.ts.map +0 -1
- package/dist/esm/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroup.d.ts +0 -35
- package/dist/esm/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroup.d.ts.map +0 -1
- package/dist/esm/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContext.d.ts +0 -6
- package/dist/esm/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContext.d.ts.map +0 -1
- package/dist/esm/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContextProvider.d.ts +0 -3
- package/dist/esm/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContextProvider.d.ts.map +0 -1
- package/dist/esm/components/PageHeader/helpers/useElementWidthObserver.d.ts.map +0 -1
- package/src/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroup.module.scss +0 -77
- package/src/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroup.spec.tsx +0 -122
- package/src/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroup.tsx +0 -235
- package/src/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContext.ts +0 -15
- package/src/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroupsContextProvider.tsx +0 -37
- /package/dist/cjs/{components/PageHeader/helpers → hooks/useElementWidthObserver}/useElementWidthObserver.d.ts +0 -0
- /package/dist/esm/{components/PageHeader/helpers → hooks/useElementWidthObserver}/useElementWidthObserver.d.ts +0 -0
- /package/src/{components/PageHeader/helpers → hooks/useElementWidthObserver}/useElementWidthObserver.tsx +0 -0
|
@@ -5,7 +5,7 @@ import { BrowserRouter as Router } from 'react-router-dom';
|
|
|
5
5
|
import { describe, expect, it, vi } from 'vitest';
|
|
6
6
|
import { noop } from '../../../helpers/utils';
|
|
7
7
|
import type { ConfirmationConfig } from '../../ConfirmDialog';
|
|
8
|
-
import { IconName } from '../../Icons';
|
|
8
|
+
import { IconName, Icons } from '../../Icons';
|
|
9
9
|
import {
|
|
10
10
|
PageHeaderAction,
|
|
11
11
|
isPageHeaderJsAction,
|
|
@@ -71,6 +71,63 @@ describe('PageHeaderAction', () => {
|
|
|
71
71
|
expect(Icon).not.toBeInTheDocument();
|
|
72
72
|
});
|
|
73
73
|
|
|
74
|
+
it('shows the icon instead of the label, which becomes its tooltip', () => {
|
|
75
|
+
const { container } = render(
|
|
76
|
+
<PageHeaderAction
|
|
77
|
+
{...defaultProps}
|
|
78
|
+
label={'Bulk Actions'}
|
|
79
|
+
icon={IconName.Bulk}
|
|
80
|
+
/>,
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
const action = container.querySelector('.container') as HTMLElement;
|
|
84
|
+
|
|
85
|
+
expect(container.querySelector('.icons')).toBeInTheDocument();
|
|
86
|
+
expect(container.querySelector('.label')).toHaveClass('hiddenLabel');
|
|
87
|
+
// Still names the action for assistive technology.
|
|
88
|
+
expect(action).toHaveTextContent('Bulk Actions');
|
|
89
|
+
expect(action).toHaveAttribute('title', 'Bulk Actions');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('shows the label of an action without an icon', () => {
|
|
93
|
+
const { container } = render(
|
|
94
|
+
<PageHeaderAction {...defaultProps} label={'Create Snapshot'} />,
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const action = container.querySelector('.container') as HTMLElement;
|
|
98
|
+
|
|
99
|
+
expect(container.querySelector('.icon')).not.toBeInTheDocument();
|
|
100
|
+
expect(container.querySelector('.label')).not.toHaveClass('hiddenLabel');
|
|
101
|
+
expect(action).not.toHaveAttribute('title');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it(`treats the icon with the enum value 0 as an icon`, () => {
|
|
105
|
+
const { container } = render(
|
|
106
|
+
<PageHeaderAction {...defaultProps} icon={IconName.Archive} />,
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
expect(IconName.Archive).toBe(0);
|
|
110
|
+
expect(container.querySelector('.icons')).toBeInTheDocument();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('shows the confirmation message in place of the icon', () => {
|
|
114
|
+
const { container } = render(
|
|
115
|
+
<PageHeaderAction
|
|
116
|
+
{...defaultProps}
|
|
117
|
+
icon={IconName.Delete}
|
|
118
|
+
confirmationMode="Simple"
|
|
119
|
+
/>,
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
const action = container.querySelector('.container') as HTMLElement;
|
|
123
|
+
fireEvent.click(action);
|
|
124
|
+
|
|
125
|
+
expect(container.querySelector('.icons')).not.toBeInTheDocument();
|
|
126
|
+
expect(container.querySelector('.label')).not.toHaveClass('hiddenLabel');
|
|
127
|
+
expect(action).not.toHaveAttribute('title');
|
|
128
|
+
expect(container.querySelector('[data-test-id="confirm"]')).toBeVisible();
|
|
129
|
+
});
|
|
130
|
+
|
|
74
131
|
it('raises onActionSelected', () => {
|
|
75
132
|
const spy = vi.fn();
|
|
76
133
|
const { container } = render(
|
|
@@ -605,6 +662,95 @@ describe('PageHeaderAction', () => {
|
|
|
605
662
|
expect(actionContainer.classList.contains('active')).toBe(true);
|
|
606
663
|
});
|
|
607
664
|
|
|
665
|
+
describe('toggled', () => {
|
|
666
|
+
it(`renders the 'Active' style when toggled is true`, () => {
|
|
667
|
+
const { container } = render(
|
|
668
|
+
<PageHeaderAction
|
|
669
|
+
label={'action-label'}
|
|
670
|
+
toggled={true}
|
|
671
|
+
onClick={vi.fn()}
|
|
672
|
+
/>,
|
|
673
|
+
);
|
|
674
|
+
|
|
675
|
+
const actionContainer = container.querySelector(
|
|
676
|
+
'.container',
|
|
677
|
+
) as HTMLElement;
|
|
678
|
+
|
|
679
|
+
expect(actionContainer.classList.contains('active')).toBe(true);
|
|
680
|
+
expect(actionContainer.classList.contains('context')).toBe(false);
|
|
681
|
+
expect(actionContainer.getAttribute('aria-pressed')).toBe('true');
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
it(`falls back to the actionType style when toggled is false`, () => {
|
|
685
|
+
const { container } = render(
|
|
686
|
+
<PageHeaderAction
|
|
687
|
+
label={'action-label'}
|
|
688
|
+
toggled={false}
|
|
689
|
+
onClick={vi.fn()}
|
|
690
|
+
/>,
|
|
691
|
+
);
|
|
692
|
+
|
|
693
|
+
const actionContainer = container.querySelector(
|
|
694
|
+
'.container',
|
|
695
|
+
) as HTMLElement;
|
|
696
|
+
|
|
697
|
+
expect(actionContainer.classList.contains('context')).toBe(true);
|
|
698
|
+
expect(actionContainer.classList.contains('active')).toBe(false);
|
|
699
|
+
expect(actionContainer.getAttribute('aria-pressed')).toBe('false');
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
it(`doesn't set 'aria-pressed' when toggled is not provided`, () => {
|
|
703
|
+
const { container } = render(
|
|
704
|
+
<PageHeaderAction label={'action-label'} onClick={vi.fn()} />,
|
|
705
|
+
);
|
|
706
|
+
|
|
707
|
+
const actionContainer = container.querySelector(
|
|
708
|
+
'.container',
|
|
709
|
+
) as HTMLElement;
|
|
710
|
+
|
|
711
|
+
expect(actionContainer.getAttribute('aria-pressed')).toBeNull();
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
it(`takes precedence over actionType when toggled is true`, () => {
|
|
715
|
+
const { container } = render(
|
|
716
|
+
<PageHeaderAction
|
|
717
|
+
label={'action-label'}
|
|
718
|
+
actionType={PageHeaderActionType.Hightlight}
|
|
719
|
+
toggled={true}
|
|
720
|
+
onClick={vi.fn()}
|
|
721
|
+
/>,
|
|
722
|
+
);
|
|
723
|
+
|
|
724
|
+
const actionContainer = container.querySelector(
|
|
725
|
+
'.container',
|
|
726
|
+
) as HTMLElement;
|
|
727
|
+
|
|
728
|
+
expect(actionContainer.classList.contains('active')).toBe(true);
|
|
729
|
+
expect(actionContainer.classList.contains('highlight')).toBe(false);
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
it(`disallows interactions when a toggled action is disabled`, () => {
|
|
733
|
+
const spy = vi.fn();
|
|
734
|
+
const { container } = render(
|
|
735
|
+
<PageHeaderAction
|
|
736
|
+
label={'action-label'}
|
|
737
|
+
toggled={true}
|
|
738
|
+
disabled={true}
|
|
739
|
+
onClick={spy}
|
|
740
|
+
/>,
|
|
741
|
+
);
|
|
742
|
+
|
|
743
|
+
const actionContainer = container.querySelector(
|
|
744
|
+
'.container',
|
|
745
|
+
) as HTMLElement;
|
|
746
|
+
|
|
747
|
+
fireEvent.click(actionContainer);
|
|
748
|
+
|
|
749
|
+
expect(actionContainer.getAttribute('disabled')).not.toBeNull();
|
|
750
|
+
expect(spy).not.toHaveBeenCalled();
|
|
751
|
+
});
|
|
752
|
+
});
|
|
753
|
+
|
|
608
754
|
it(`disallows interactions when action is set to disabled`, () => {
|
|
609
755
|
const spy = vi.fn();
|
|
610
756
|
const { container } = render(
|
|
@@ -720,6 +866,52 @@ describe('PageHeaderAction', () => {
|
|
|
720
866
|
expect(link.target).toBe('_blank');
|
|
721
867
|
});
|
|
722
868
|
|
|
869
|
+
it('shows the external icon, with the label as its tooltip, for a link opening in a new tab', () => {
|
|
870
|
+
const { container } = render(
|
|
871
|
+
<Router>
|
|
872
|
+
<PageHeaderAction {...defaultProps} path="/test" openInNewTab />
|
|
873
|
+
</Router>,
|
|
874
|
+
);
|
|
875
|
+
|
|
876
|
+
const link = container.querySelector('a') as HTMLAnchorElement;
|
|
877
|
+
|
|
878
|
+
expect(container.querySelector('.icons')).toBeInTheDocument();
|
|
879
|
+
expect(link).toHaveAttribute('title', 'Action Label');
|
|
880
|
+
});
|
|
881
|
+
|
|
882
|
+
it('shows the label of a link without an icon', () => {
|
|
883
|
+
const { container } = render(
|
|
884
|
+
<Router>
|
|
885
|
+
<PageHeaderAction {...defaultProps} path="/test" />
|
|
886
|
+
</Router>,
|
|
887
|
+
);
|
|
888
|
+
|
|
889
|
+
const link = container.querySelector('a') as HTMLAnchorElement;
|
|
890
|
+
|
|
891
|
+
expect(container.querySelector('.icon')).not.toBeInTheDocument();
|
|
892
|
+
expect(link).not.toHaveAttribute('title');
|
|
893
|
+
});
|
|
894
|
+
|
|
895
|
+
it(`treats the icon with the enum value 0 as an icon`, () => {
|
|
896
|
+
const { container } = render(
|
|
897
|
+
<Router>
|
|
898
|
+
<PageHeaderAction
|
|
899
|
+
{...defaultProps}
|
|
900
|
+
path="/test"
|
|
901
|
+
openInNewTab
|
|
902
|
+
icon={IconName.Archive}
|
|
903
|
+
/>
|
|
904
|
+
</Router>,
|
|
905
|
+
);
|
|
906
|
+
|
|
907
|
+
const { container: archive } = render(<Icons icon={IconName.Archive} />);
|
|
908
|
+
|
|
909
|
+
// The icon of the action is used instead of the external icon.
|
|
910
|
+
expect(container.querySelector('.icons')?.innerHTML).toBe(
|
|
911
|
+
archive.querySelector('.icons')?.innerHTML,
|
|
912
|
+
);
|
|
913
|
+
});
|
|
914
|
+
|
|
723
915
|
it('does not open link in a new tab if openInNewTab is false', () => {
|
|
724
916
|
const { container } = render(
|
|
725
917
|
<Router>
|
|
@@ -30,6 +30,11 @@ const meta: Meta<typeof PageHeaderAction> = {
|
|
|
30
30
|
type: 'text',
|
|
31
31
|
},
|
|
32
32
|
},
|
|
33
|
+
toggled: {
|
|
34
|
+
control: {
|
|
35
|
+
type: 'boolean',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
33
38
|
},
|
|
34
39
|
decorators: [
|
|
35
40
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
@@ -51,3 +56,34 @@ export const CustomImage: StoryObj<typeof PageHeaderAction> = {
|
|
|
51
56
|
actionType: PageHeaderActionType.Context,
|
|
52
57
|
},
|
|
53
58
|
};
|
|
59
|
+
|
|
60
|
+
export const Toggled: StoryObj<typeof PageHeaderAction> = {
|
|
61
|
+
args: {
|
|
62
|
+
icon: IconName.Bulk,
|
|
63
|
+
label: 'Toggle',
|
|
64
|
+
toggled: true,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const WithoutIcon: StoryObj<typeof PageHeaderAction> = {
|
|
69
|
+
name: 'Label without Icon',
|
|
70
|
+
parameters: {
|
|
71
|
+
docs: {
|
|
72
|
+
description: {
|
|
73
|
+
story: `An action without an icon shows its label instead, which wraps onto further rows and is cut after the fourth one.
|
|
74
|
+
The label wraps even where the surrounding layout prevents wrapping, which the story shows by rendering the action in such a container.`,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
args: {
|
|
79
|
+
label: 'Create Snapshot and Publish Episodes',
|
|
80
|
+
},
|
|
81
|
+
decorators: [
|
|
82
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
83
|
+
(Story) => (
|
|
84
|
+
<div style={{ whiteSpace: 'nowrap' }}>
|
|
85
|
+
<Story />
|
|
86
|
+
</div>
|
|
87
|
+
),
|
|
88
|
+
],
|
|
89
|
+
};
|
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
import clsx from 'clsx';
|
|
2
|
-
import React, {
|
|
2
|
+
import React, { type PropsWithChildren } from 'react';
|
|
3
3
|
import { Link } from 'react-router-dom';
|
|
4
4
|
import { noop } from '../../../helpers/utils';
|
|
5
|
-
import {
|
|
6
|
-
ConfirmDialog,
|
|
7
|
-
type ConfirmationConfig,
|
|
8
|
-
type ConfirmationMode,
|
|
9
|
-
useConfirmationDelay,
|
|
10
|
-
} from '../../ConfirmDialog';
|
|
5
|
+
import { useConfirmableAction } from '../../ConfirmDialog';
|
|
11
6
|
import { IconName, Icons } from '../../Icons';
|
|
12
7
|
import { ImgElement } from '../../VisualElements';
|
|
13
8
|
import {
|
|
@@ -18,31 +13,14 @@ import {
|
|
|
18
13
|
} from './PageHeaderAction.model';
|
|
19
14
|
import classes from './PageHeaderAction.module.scss';
|
|
20
15
|
|
|
21
|
-
/**
|
|
22
|
-
* Simple Confirm message
|
|
23
|
-
* @example
|
|
24
|
-
* <SimpleConfirmDialog />
|
|
25
|
-
*/
|
|
26
|
-
const SimpleConfirmDialog: React.FC<{
|
|
27
|
-
onConfirmOpen: ConfirmationConfig['onConfirmOpen'];
|
|
28
|
-
data: { mode: ConfirmationMode };
|
|
29
|
-
}> = ({ onConfirmOpen, data }) => {
|
|
30
|
-
useEffect(() => {
|
|
31
|
-
onConfirmOpen && onConfirmOpen(true, data);
|
|
32
|
-
return () => {
|
|
33
|
-
onConfirmOpen && onConfirmOpen(false, data);
|
|
34
|
-
};
|
|
35
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
36
|
-
}, []);
|
|
37
|
-
|
|
38
|
-
return <span data-test-id="confirm">Click again to confirm</span>;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
16
|
/**
|
|
42
17
|
* Used to create actions for the PageHeader component.
|
|
43
18
|
* To generate anchor tag provide `path` property
|
|
44
19
|
* To generate HTML element with event handler provide `onClick` property
|
|
45
20
|
* (and optionally `confirmationMode`, `confirmationConfig`)
|
|
21
|
+
*
|
|
22
|
+
* An action shows its icon if it has one, and its label otherwise. The label of
|
|
23
|
+
* an action with an icon is shown as a tooltip.
|
|
46
24
|
* @example
|
|
47
25
|
* // Action with JS clickHandler
|
|
48
26
|
* <PageHeaderAction label={'Action Label'} onClick={clickHandler} />
|
|
@@ -71,47 +49,27 @@ const PageHeaderJSAction: React.FC<PageHeaderJsActionProps> = ({
|
|
|
71
49
|
className,
|
|
72
50
|
onClick = noop,
|
|
73
51
|
tag,
|
|
52
|
+
toggled,
|
|
74
53
|
}) => {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const
|
|
54
|
+
// A toggled-on action always renders in the 'Active' style, otherwise the
|
|
55
|
+
// explicit (or default) actionType decides the appearance.
|
|
56
|
+
const visualActionType = toggled ? PageHeaderActionType.Active : actionType;
|
|
57
|
+
|
|
58
|
+
const {
|
|
59
|
+
isConfirming,
|
|
60
|
+
isSimpleConfirmation,
|
|
61
|
+
onTriggerClicked,
|
|
62
|
+
triggerProps,
|
|
63
|
+
simpleConfirmation,
|
|
64
|
+
advancedConfirmation,
|
|
65
|
+
} = useConfirmableAction<HTMLButtonElement>({
|
|
78
66
|
confirmationMode,
|
|
79
|
-
|
|
80
|
-
|
|
67
|
+
confirmationConfig,
|
|
68
|
+
onConfirmed: onClick,
|
|
81
69
|
});
|
|
82
70
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const onClickHandler = (
|
|
87
|
-
event: React.MouseEvent<HTMLButtonElement, MouseEvent>,
|
|
88
|
-
): void => {
|
|
89
|
-
event.preventDefault();
|
|
90
|
-
|
|
91
|
-
switch (confirmationMode) {
|
|
92
|
-
case 'Simple':
|
|
93
|
-
!confirmation
|
|
94
|
-
? setConfirmation(true)
|
|
95
|
-
: (onClick(), setConfirmation(false));
|
|
96
|
-
break;
|
|
97
|
-
case 'Advanced':
|
|
98
|
-
setConfirmation(true);
|
|
99
|
-
break;
|
|
100
|
-
default:
|
|
101
|
-
onClick();
|
|
102
|
-
setConfirmation(false);
|
|
103
|
-
break;
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
const onConfirmOpenHandler = (
|
|
108
|
-
isOpen: boolean,
|
|
109
|
-
data: { mode: ConfirmationMode },
|
|
110
|
-
): void => {
|
|
111
|
-
if (confirmationMode === 'Advanced' && onConfirmOpen !== undefined) {
|
|
112
|
-
onConfirmOpen(isOpen, data);
|
|
113
|
-
}
|
|
114
|
-
};
|
|
71
|
+
// A pending confirmation shows its message, which takes the place of the icon.
|
|
72
|
+
const visibleIcon = isConfirming ? undefined : icon;
|
|
115
73
|
|
|
116
74
|
return (
|
|
117
75
|
<>
|
|
@@ -119,70 +77,36 @@ const PageHeaderJSAction: React.FC<PageHeaderJsActionProps> = ({
|
|
|
119
77
|
className={clsx(
|
|
120
78
|
classes.container,
|
|
121
79
|
{
|
|
122
|
-
[classes.context]:
|
|
123
|
-
|
|
124
|
-
[classes.
|
|
125
|
-
[classes.
|
|
80
|
+
[classes.context]:
|
|
81
|
+
visualActionType === PageHeaderActionType.Context,
|
|
82
|
+
[classes.active]: visualActionType === PageHeaderActionType.Active,
|
|
83
|
+
[classes.highlight]:
|
|
84
|
+
visualActionType === PageHeaderActionType.Hightlight,
|
|
85
|
+
[classes.hasConfirm]: isConfirming,
|
|
126
86
|
},
|
|
127
87
|
'page-header-action-container',
|
|
128
88
|
className,
|
|
129
89
|
)} // Show different bg-color when confirmation is displayed
|
|
130
|
-
|
|
131
|
-
onClick={
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
onMouseOver={() => {
|
|
135
|
-
// Needed in cases where the mouse enters the component after another confirmation dialog was dismissed
|
|
136
|
-
if (!isMouseOver) {
|
|
137
|
-
setIsMouseOver(true);
|
|
138
|
-
}
|
|
90
|
+
{...triggerProps}
|
|
91
|
+
onClick={(event) => {
|
|
92
|
+
event.preventDefault();
|
|
93
|
+
onTriggerClicked();
|
|
139
94
|
}}
|
|
140
95
|
disabled={disabled}
|
|
96
|
+
title={visibleIcon !== undefined ? label : undefined}
|
|
97
|
+
aria-pressed={toggled}
|
|
141
98
|
data-test-id="action"
|
|
99
|
+
data-test-toggled={toggled}
|
|
142
100
|
>
|
|
143
|
-
<
|
|
144
|
-
{
|
|
145
|
-
|
|
146
|
-
<ImgElement
|
|
147
|
-
src={icon}
|
|
148
|
-
{...(imgAlt
|
|
149
|
-
? { alt: imgAlt ?? `${label} icon` }
|
|
150
|
-
: { decorative: true })}
|
|
151
|
-
/>
|
|
152
|
-
) : (
|
|
153
|
-
<Icons icon={icon} className={classes.pageHeaderActionsIcons} />
|
|
154
|
-
))}
|
|
155
|
-
</div>
|
|
156
|
-
<div className={classes.label}>
|
|
157
|
-
{confirmation && confirmationMode === 'Simple' ? (
|
|
158
|
-
<SimpleConfirmDialog
|
|
159
|
-
onConfirmOpen={onConfirmOpen}
|
|
160
|
-
data={{ mode: 'Simple' }}
|
|
161
|
-
/>
|
|
101
|
+
<PageHeaderActionContent icon={visibleIcon} imgAlt={imgAlt} tag={tag}>
|
|
102
|
+
{isSimpleConfirmation ? (
|
|
103
|
+
simpleConfirmation
|
|
162
104
|
) : (
|
|
163
105
|
<span data-test-id="label">{label}</span>
|
|
164
106
|
)}
|
|
165
|
-
</
|
|
166
|
-
{tag && <div className={classes.tag}>{tag}</div>}
|
|
107
|
+
</PageHeaderActionContent>
|
|
167
108
|
</button>
|
|
168
|
-
{
|
|
169
|
-
<ConfirmDialog
|
|
170
|
-
referenceElement={referenceElement}
|
|
171
|
-
title={title}
|
|
172
|
-
cancelButtonText={cancelButtonText}
|
|
173
|
-
confirmButtonText={confirmButtonText}
|
|
174
|
-
onCancel={() => setConfirmation(false)}
|
|
175
|
-
onConfirm={() => {
|
|
176
|
-
onClick();
|
|
177
|
-
setConfirmation(false);
|
|
178
|
-
}}
|
|
179
|
-
onConfirmOpen={(isOpen) =>
|
|
180
|
-
onConfirmOpenHandler(isOpen, { mode: 'Advanced' })
|
|
181
|
-
}
|
|
182
|
-
>
|
|
183
|
-
{body}
|
|
184
|
-
</ConfirmDialog>
|
|
185
|
-
)}
|
|
109
|
+
{advancedConfirmation}
|
|
186
110
|
</>
|
|
187
111
|
);
|
|
188
112
|
};
|
|
@@ -199,7 +123,8 @@ const PageHeaderNavigationAction: React.FC<PageHeaderNavigationActionProps> = ({
|
|
|
199
123
|
path,
|
|
200
124
|
tag,
|
|
201
125
|
}) => {
|
|
202
|
-
const headerIcon =
|
|
126
|
+
const headerIcon =
|
|
127
|
+
icon !== undefined ? icon : openInNewTab ? IconName.External : undefined;
|
|
203
128
|
|
|
204
129
|
return (
|
|
205
130
|
<Link
|
|
@@ -211,26 +136,52 @@ const PageHeaderNavigationAction: React.FC<PageHeaderNavigationActionProps> = ({
|
|
|
211
136
|
data-test-id="action"
|
|
212
137
|
to={path}
|
|
213
138
|
target={openInNewTab ? '_blank' : undefined}
|
|
139
|
+
title={headerIcon !== undefined ? label : undefined}
|
|
214
140
|
>
|
|
141
|
+
<PageHeaderActionContent icon={headerIcon} imgAlt={imgAlt} tag={tag}>
|
|
142
|
+
<span data-test-id="label">{label}</span>
|
|
143
|
+
</PageHeaderActionContent>
|
|
144
|
+
</Link>
|
|
145
|
+
);
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* The icon of an action, or its label if it has no icon.
|
|
150
|
+
*
|
|
151
|
+
* A label that gives way to an icon is kept in place, transparent, so that it
|
|
152
|
+
* still names the action for assistive technology and receives the clicks on
|
|
153
|
+
* the action.
|
|
154
|
+
*/
|
|
155
|
+
const PageHeaderActionContent: React.FC<
|
|
156
|
+
PropsWithChildren<{
|
|
157
|
+
icon?: IconName | string;
|
|
158
|
+
imgAlt?: string;
|
|
159
|
+
tag?: string;
|
|
160
|
+
}>
|
|
161
|
+
> = ({ icon, imgAlt, tag, children }) => (
|
|
162
|
+
<>
|
|
163
|
+
{icon !== undefined && (
|
|
215
164
|
<div className={classes.icon}>
|
|
216
|
-
{typeof
|
|
165
|
+
{typeof icon === 'string' ? (
|
|
217
166
|
<ImgElement
|
|
218
|
-
src={
|
|
219
|
-
{...(imgAlt
|
|
220
|
-
? { alt: imgAlt ?? `${label} icon` }
|
|
221
|
-
: { decorative: true })}
|
|
167
|
+
src={icon}
|
|
168
|
+
{...(imgAlt ? { alt: imgAlt } : { decorative: true as const })}
|
|
222
169
|
/>
|
|
223
170
|
) : (
|
|
224
|
-
<Icons icon={
|
|
171
|
+
<Icons icon={icon} className={classes.pageHeaderActionsIcons} />
|
|
225
172
|
)}
|
|
226
173
|
</div>
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
174
|
+
)}
|
|
175
|
+
<div
|
|
176
|
+
className={clsx(classes.label, {
|
|
177
|
+
[classes.hiddenLabel]: icon !== undefined,
|
|
178
|
+
})}
|
|
179
|
+
>
|
|
180
|
+
{children}
|
|
181
|
+
</div>
|
|
182
|
+
{tag && <div className={classes.tag}>{tag}</div>}
|
|
183
|
+
</>
|
|
184
|
+
);
|
|
234
185
|
|
|
235
186
|
/**
|
|
236
187
|
* Determines whether an action data object is a PageHeaderNavigationActionProps object.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { IconName } from '../Icons';
|
|
3
|
+
import { PageHeaderActionType } from './PageHeaderAction';
|
|
4
|
+
import { toActionBarAction } from './helpers';
|
|
5
|
+
|
|
6
|
+
describe('toActionBarAction', () => {
|
|
7
|
+
it('keeps what the action bar renders of an action with a handler', () => {
|
|
8
|
+
const onClick = vi.fn();
|
|
9
|
+
|
|
10
|
+
expect(
|
|
11
|
+
toActionBarAction({
|
|
12
|
+
label: 'Delete',
|
|
13
|
+
icon: IconName.Delete,
|
|
14
|
+
imgAlt: 'Delete icon',
|
|
15
|
+
tag: 'BETA',
|
|
16
|
+
disabled: true,
|
|
17
|
+
toggled: false,
|
|
18
|
+
confirmationMode: 'Advanced',
|
|
19
|
+
confirmationConfig: { title: 'Delete the items?' },
|
|
20
|
+
onClick,
|
|
21
|
+
}),
|
|
22
|
+
).toEqual({
|
|
23
|
+
label: 'Delete',
|
|
24
|
+
icon: IconName.Delete,
|
|
25
|
+
imgAlt: 'Delete icon',
|
|
26
|
+
tag: 'BETA',
|
|
27
|
+
disabled: true,
|
|
28
|
+
toggled: false,
|
|
29
|
+
confirmationMode: 'Advanced',
|
|
30
|
+
confirmationConfig: { title: 'Delete the items?' },
|
|
31
|
+
onClick,
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('keeps the path of a navigation action', () => {
|
|
36
|
+
expect(
|
|
37
|
+
toActionBarAction({
|
|
38
|
+
label: 'Open',
|
|
39
|
+
path: '/details',
|
|
40
|
+
openInNewTab: true,
|
|
41
|
+
}),
|
|
42
|
+
).toEqual({
|
|
43
|
+
label: 'Open',
|
|
44
|
+
path: '/details',
|
|
45
|
+
openInNewTab: true,
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('drops the properties that only style the page header', () => {
|
|
50
|
+
const action = toActionBarAction({
|
|
51
|
+
label: 'Delete',
|
|
52
|
+
actionType: PageHeaderActionType.Hightlight,
|
|
53
|
+
className: 'custom-class',
|
|
54
|
+
onClick: vi.fn(),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
expect(action).not.toHaveProperty('actionType');
|
|
58
|
+
expect(action).not.toHaveProperty('className');
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ActionBarAction } from '../ActionBar';
|
|
2
|
+
import { isPageHeaderNavigationAction } from './PageHeaderAction/PageHeaderAction';
|
|
3
|
+
import type { PageHeaderActionProps } from './PageHeaderAction/PageHeaderAction.model';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Converts an action of the page header into an action of the ActionBar.
|
|
7
|
+
* `actionType` and `className` only style the page header, so they are dropped.
|
|
8
|
+
* @param action the action to convert
|
|
9
|
+
*/
|
|
10
|
+
export const toActionBarAction = (
|
|
11
|
+
action: PageHeaderActionProps,
|
|
12
|
+
): ActionBarAction => {
|
|
13
|
+
const { label, icon, imgAlt, tag, disabled } = action;
|
|
14
|
+
|
|
15
|
+
return isPageHeaderNavigationAction(action)
|
|
16
|
+
? {
|
|
17
|
+
label,
|
|
18
|
+
icon,
|
|
19
|
+
imgAlt,
|
|
20
|
+
tag,
|
|
21
|
+
disabled,
|
|
22
|
+
path: action.path,
|
|
23
|
+
openInNewTab: action.openInNewTab,
|
|
24
|
+
}
|
|
25
|
+
: {
|
|
26
|
+
label,
|
|
27
|
+
icon,
|
|
28
|
+
imgAlt,
|
|
29
|
+
tag,
|
|
30
|
+
disabled,
|
|
31
|
+
toggled: action.toggled,
|
|
32
|
+
confirmationMode: action.confirmationMode,
|
|
33
|
+
confirmationConfig: action.confirmationConfig,
|
|
34
|
+
onClick: action.onClick,
|
|
35
|
+
};
|
|
36
|
+
};
|
package/src/components/index.ts
CHANGED
package/src/hooks/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from './models';
|
|
2
2
|
export { useClickOutside } from './useClickOutside/useClickOutside';
|
|
3
3
|
export { useDebounce } from './useDebounce/useDebounce';
|
|
4
|
+
export { useElementWidthObserver } from './useElementWidthObserver/useElementWidthObserver';
|
|
4
5
|
export { useExpand } from './useExpand/useExpand';
|
|
5
6
|
export { useReactRouterPause } from './useReactRouterPause/useReactRouterPause';
|
|
6
7
|
export { useValueOrOnDemand } from './useValueOrOnDemand/useValueOrOnDemand';
|