@axinom/mosaic-ui 0.32.0-rc.1 → 0.32.0-rc.11

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.
Files changed (100) hide show
  1. package/dist/components/Actions/Action/Action.d.ts.map +1 -1
  2. package/dist/components/Actions/Actions.models.d.ts +4 -16
  3. package/dist/components/Actions/Actions.models.d.ts.map +1 -1
  4. package/dist/components/Buttons/Button/Button.d.ts +9 -10
  5. package/dist/components/Buttons/Button/Button.d.ts.map +1 -1
  6. package/dist/components/Buttons/Button/Button.model.d.ts +21 -0
  7. package/dist/components/Buttons/Button/Button.model.d.ts.map +1 -0
  8. package/dist/components/Buttons/Button/index.d.ts +3 -0
  9. package/dist/components/Buttons/Button/index.d.ts.map +1 -0
  10. package/dist/components/Buttons/Button.model.d.ts +69 -7
  11. package/dist/components/Buttons/Button.model.d.ts.map +1 -1
  12. package/dist/components/Buttons/CompositeButton/CompositeButton.d.ts +5 -14
  13. package/dist/components/Buttons/CompositeButton/CompositeButton.d.ts.map +1 -1
  14. package/dist/components/Buttons/CompositeButton/CompositeButton.model.d.ts +20 -0
  15. package/dist/components/Buttons/CompositeButton/CompositeButton.model.d.ts.map +1 -0
  16. package/dist/components/Buttons/CompositeButton/index.d.ts +3 -0
  17. package/dist/components/Buttons/CompositeButton/index.d.ts.map +1 -0
  18. package/dist/components/Buttons/TextButton/TextButton.d.ts +5 -9
  19. package/dist/components/Buttons/TextButton/TextButton.d.ts.map +1 -1
  20. package/dist/components/Buttons/TextButton/TextButton.model.d.ts +18 -0
  21. package/dist/components/Buttons/TextButton/TextButton.model.d.ts.map +1 -0
  22. package/dist/components/Buttons/TextButton/index.d.ts +3 -0
  23. package/dist/components/Buttons/TextButton/index.d.ts.map +1 -0
  24. package/dist/components/Buttons/index.d.ts +3 -3
  25. package/dist/components/Buttons/index.d.ts.map +1 -1
  26. package/dist/components/Explorer/Explorer.d.ts.map +1 -1
  27. package/dist/components/Explorer/Explorer.model.d.ts +2 -2
  28. package/dist/components/Explorer/Explorer.model.d.ts.map +1 -1
  29. package/dist/components/Explorer/NavigationExplorer/NavigationExplorer.d.ts +13 -3
  30. package/dist/components/Explorer/NavigationExplorer/NavigationExplorer.d.ts.map +1 -1
  31. package/dist/components/FormElements/ToggleButton/ToggleButton.d.ts +2 -2
  32. package/dist/components/FormElements/ToggleButton/ToggleButton.d.ts.map +1 -1
  33. package/dist/components/List/List.d.ts +1 -1
  34. package/dist/components/List/List.d.ts.map +1 -1
  35. package/dist/components/List/ListRow/ListRow.d.ts.map +1 -1
  36. package/dist/components/PageHeader/PageHeader.d.ts +1 -22
  37. package/dist/components/PageHeader/PageHeader.d.ts.map +1 -1
  38. package/dist/components/PageHeader/PageHeader.model.d.ts +23 -0
  39. package/dist/components/PageHeader/PageHeader.model.d.ts.map +1 -0
  40. package/dist/components/PageHeader/PageHeaderAction/PageHeaderAction.d.ts +20 -33
  41. package/dist/components/PageHeader/PageHeaderAction/PageHeaderAction.d.ts.map +1 -1
  42. package/dist/components/PageHeader/PageHeaderAction/PageHeaderAction.model.d.ts +47 -0
  43. package/dist/components/PageHeader/PageHeaderAction/PageHeaderAction.model.d.ts.map +1 -0
  44. package/dist/components/PageHeader/PageHeaderAction/index.d.ts +3 -0
  45. package/dist/components/PageHeader/PageHeaderAction/index.d.ts.map +1 -0
  46. package/dist/components/PageHeader/PageHeaderBulkActions/PageHeaderBulkActions.d.ts +2 -2
  47. package/dist/components/PageHeader/PageHeaderBulkActions/PageHeaderBulkActions.d.ts.map +1 -1
  48. package/dist/components/PageHeader/index.d.ts +3 -2
  49. package/dist/components/PageHeader/index.d.ts.map +1 -1
  50. package/dist/components/models.d.ts +22 -0
  51. package/dist/components/models.d.ts.map +1 -1
  52. package/dist/index.es.js +3 -3
  53. package/dist/index.es.js.map +1 -1
  54. package/dist/index.js +3 -3
  55. package/dist/index.js.map +1 -1
  56. package/package.json +3 -3
  57. package/src/components/Actions/Action/Action.spec.tsx +16 -2
  58. package/src/components/Actions/Action/Action.tsx +6 -3
  59. package/src/components/Actions/Actions.models.ts +4 -23
  60. package/src/components/Buttons/Button/Button.model.ts +30 -0
  61. package/src/components/Buttons/Button/Button.scss +3 -1
  62. package/src/components/Buttons/Button/Button.spec.tsx +254 -83
  63. package/src/components/Buttons/Button/Button.stories.tsx +20 -0
  64. package/src/components/Buttons/Button/Button.tsx +103 -32
  65. package/src/components/Buttons/Button/index.ts +2 -0
  66. package/src/components/Buttons/Button.model.ts +84 -9
  67. package/src/components/Buttons/CompositeButton/CompositeButton.model.ts +32 -0
  68. package/src/components/Buttons/CompositeButton/CompositeButton.scss +6 -1
  69. package/src/components/Buttons/CompositeButton/CompositeButton.spec.tsx +277 -89
  70. package/src/components/Buttons/CompositeButton/CompositeButton.stories.tsx +20 -0
  71. package/src/components/Buttons/CompositeButton/CompositeButton.tsx +94 -30
  72. package/src/components/Buttons/CompositeButton/index.ts +2 -0
  73. package/src/components/Buttons/TextButton/TextButton.model.ts +27 -0
  74. package/src/components/Buttons/TextButton/TextButton.scss +3 -1
  75. package/src/components/Buttons/TextButton/TextButton.spec.tsx +198 -87
  76. package/src/components/Buttons/TextButton/TextButton.stories.tsx +20 -0
  77. package/src/components/Buttons/TextButton/TextButton.tsx +74 -20
  78. package/src/components/Buttons/TextButton/index.ts +2 -0
  79. package/src/components/Buttons/index.ts +3 -6
  80. package/src/components/Explorer/Explorer.model.ts +2 -2
  81. package/src/components/Explorer/Explorer.tsx +21 -16
  82. package/src/components/Explorer/NavigationExplorer/NavigationExplorer.tsx +32 -11
  83. package/src/components/FormElements/CustomTags/CustomTags.spec.tsx +26 -16
  84. package/src/components/FormElements/ToggleButton/ToggleButton.tsx +3 -3
  85. package/src/components/List/List.spec.tsx +23 -0
  86. package/src/components/List/List.stories.tsx +8 -0
  87. package/src/components/List/List.tsx +15 -3
  88. package/src/components/List/ListRow/ListRow.tsx +18 -13
  89. package/src/components/PageHeader/PageHeader.model.ts +23 -0
  90. package/src/components/PageHeader/PageHeader.stories.tsx +2 -1
  91. package/src/components/PageHeader/PageHeader.tsx +2 -26
  92. package/src/components/PageHeader/PageHeaderAction/PageHeaderAction.model.ts +60 -0
  93. package/src/components/PageHeader/PageHeaderAction/PageHeaderAction.spec.tsx +550 -383
  94. package/src/components/PageHeader/PageHeaderAction/PageHeaderAction.stories.tsx +12 -1
  95. package/src/components/PageHeader/PageHeaderAction/PageHeaderAction.tsx +95 -45
  96. package/src/components/PageHeader/PageHeaderAction/index.ts +2 -0
  97. package/src/components/PageHeader/PageHeaderBulkActions/PageHeaderBulkActions.spec.tsx +2 -2
  98. package/src/components/PageHeader/PageHeaderBulkActions/PageHeaderBulkActions.tsx +56 -43
  99. package/src/components/PageHeader/index.ts +3 -2
  100. package/src/components/models.ts +30 -0
@@ -1,135 +1,246 @@
1
1
  import { mount, shallow } from 'enzyme';
2
2
  import React from 'react';
3
+ import { Link, BrowserRouter as Router } from 'react-router-dom';
3
4
  import { ButtonContext } from '../Button.model';
4
5
  import { TextButton } from './TextButton';
5
6
 
6
- describe('Button', () => {
7
- it('renders the component without crashing', () => {
8
- const wrapper = shallow(<TextButton />);
7
+ describe('TextButton', () => {
8
+ describe('TextContextButton', () => {
9
+ it('renders the component without crashing', () => {
10
+ const wrapper = shallow(<TextButton />);
9
11
 
10
- expect(wrapper).toBeTruthy();
11
- });
12
+ expect(wrapper).toBeTruthy();
13
+ });
12
14
 
13
- it('raises the onButtonClicked event', () => {
14
- const spy = jest.fn();
15
- const wrapper = shallow(<TextButton onButtonClicked={spy} />);
15
+ it('raises the onButtonClicked event', () => {
16
+ const spy = jest.fn();
17
+ const wrapper = mount(<TextButton onButtonClicked={spy} />);
16
18
 
17
- wrapper.simulate('click');
19
+ wrapper.simulate('click');
18
20
 
19
- expect(spy).toHaveBeenCalledTimes(1);
20
- });
21
+ expect(spy).toHaveBeenCalledTimes(1);
22
+ });
21
23
 
22
- it('creates a class based off of the className prop', () => {
23
- const mockClassName = 'test-class';
24
- const wrapper = shallow(<TextButton className={mockClassName} />);
24
+ it('creates a class based off of the className prop', () => {
25
+ const mockClassName = 'test-class';
26
+ const wrapper = mount(<TextButton className={mockClassName} />);
25
27
 
26
- const button = wrapper.find('button');
28
+ const button = wrapper.find('button');
27
29
 
28
- expect(button.hasClass(mockClassName)).toBe(true);
29
- });
30
+ expect(button.hasClass(mockClassName)).toBe(true);
31
+ });
30
32
 
31
- it(`button 'type' must be 'button' by default`, () => {
32
- const wrapper = shallow(<TextButton />);
33
+ it(`button 'type' must be 'button' by default`, () => {
34
+ const wrapper = mount(<TextButton />);
33
35
 
34
- const buttonType = wrapper.find('button').prop('type');
36
+ const buttonType = wrapper.find('button').prop('type');
35
37
 
36
- expect(buttonType).toBe('button');
37
- });
38
+ expect(buttonType).toBe('button');
39
+ });
38
40
 
39
- it('accepts type html attribute and width/height styles', () => {
40
- const mockType = 'submit';
41
- const mockHeight = '75px';
42
- const mockWidth = '80px';
41
+ it('accepts type html attribute and width/height styles', () => {
42
+ const mockType = 'submit';
43
+ const mockHeight = '75px';
44
+ const mockWidth = '80px';
43
45
 
44
- const wrapper = shallow(<TextButton />);
46
+ const wrapper = mount(<TextButton />);
45
47
 
46
- let buttonType = wrapper.find('button').prop('type');
47
- let buttonStyles = wrapper
48
- .find('button')
49
- .prop('style') as React.CSSProperties;
48
+ let buttonType = wrapper.find('button').prop('type');
49
+ let buttonStyles = wrapper
50
+ .find('button')
51
+ .prop('style') as React.CSSProperties;
50
52
 
51
- expect(buttonType).toBe('button');
52
- expect(buttonStyles.height).toBeUndefined();
53
- expect(buttonStyles.width).toBeUndefined();
53
+ expect(buttonType).toBe('button');
54
+ expect(buttonStyles.height).toBeUndefined();
55
+ expect(buttonStyles.width).toBeUndefined();
54
56
 
55
- wrapper.setProps({ type: mockType, height: mockHeight, width: mockWidth });
56
- wrapper.update();
57
+ wrapper.setProps({
58
+ type: mockType,
59
+ height: mockHeight,
60
+ width: mockWidth,
61
+ });
62
+ wrapper.update();
57
63
 
58
- buttonType = wrapper.find('button').prop('type');
59
- buttonStyles = wrapper.find('button').prop('style') as React.CSSProperties;
64
+ buttonType = wrapper.find('button').prop('type');
65
+ buttonStyles = wrapper
66
+ .find('button')
67
+ .prop('style') as React.CSSProperties;
60
68
 
61
- expect(buttonType).toBe(mockType);
62
- expect(buttonStyles.height).toBe(mockHeight);
63
- expect(buttonStyles.width).toBe(mockWidth);
64
- });
69
+ expect(buttonType).toBe(mockType);
70
+ expect(buttonStyles.height).toBe(mockHeight);
71
+ expect(buttonStyles.width).toBe(mockWidth);
72
+ });
65
73
 
66
- it('allows the button to be enabled by default', () => {
67
- const spy = jest.fn();
68
- const wrapper = mount(<TextButton onButtonClicked={spy} />);
74
+ it('allows the button to be enabled by default', () => {
75
+ const spy = jest.fn();
76
+ const wrapper = mount(<TextButton onButtonClicked={spy} />);
69
77
 
70
- const button = wrapper.find('button');
78
+ const button = wrapper.find('button');
71
79
 
72
- wrapper.simulate('click');
80
+ wrapper.simulate('click');
73
81
 
74
- expect(button.prop('disabled')).toBe(false);
75
- expect(spy).toHaveBeenCalledTimes(1);
76
- });
82
+ expect(button.prop('disabled')).toBe(false);
83
+ expect(spy).toHaveBeenCalledTimes(1);
84
+ });
77
85
 
78
- it('allows button to be disabled', () => {
79
- const spy = jest.fn();
80
- const wrapper = mount(<TextButton disabled={true} onButtonClicked={spy} />);
86
+ it('allows button to be disabled', () => {
87
+ const spy = jest.fn();
88
+ const wrapper = mount(
89
+ <TextButton disabled={true} onButtonClicked={spy} />,
90
+ );
81
91
 
82
- const button = wrapper.find('button');
92
+ const button = wrapper.find('button');
83
93
 
84
- wrapper.simulate('click');
94
+ wrapper.simulate('click');
85
95
 
86
- expect(button.prop('disabled')).toBe(true);
87
- expect(spy).not.toHaveBeenCalled();
88
- });
96
+ expect(button.prop('disabled')).toBe(true);
97
+ expect(spy).not.toHaveBeenCalled();
98
+ });
89
99
 
90
- it('default should have a context class', () => {
91
- const wrapper = shallow(<TextButton />);
100
+ it('default should have a context class', () => {
101
+ const wrapper = mount(<TextButton />);
92
102
 
93
- const button = wrapper.find('button');
103
+ const button = wrapper.find('button');
94
104
 
95
- expect(button.hasClass('context')).toBe(true);
96
- });
105
+ expect(button.hasClass('context')).toBe(true);
106
+ });
97
107
 
98
- it('context button type should have a context class', () => {
99
- const wrapper = shallow(
100
- <TextButton buttonContext={ButtonContext.Context} />,
101
- );
108
+ it('context button type should have a context class', () => {
109
+ const wrapper = mount(
110
+ <TextButton buttonContext={ButtonContext.Context} />,
111
+ );
102
112
 
103
- const button = wrapper.find('button');
113
+ const button = wrapper.find('button');
104
114
 
105
- expect(button.hasClass('context')).toBe(true);
106
- });
115
+ expect(button.hasClass('context')).toBe(true);
116
+ });
107
117
 
108
- it('active button type should have a active class', () => {
109
- const wrapper = shallow(
110
- <TextButton buttonContext={ButtonContext.Active} />,
111
- );
118
+ it('active button type should have a active class', () => {
119
+ const wrapper = mount(
120
+ <TextButton buttonContext={ButtonContext.Active} />,
121
+ );
112
122
 
113
- const button = wrapper.find('button');
123
+ const button = wrapper.find('button');
114
124
 
115
- expect(button.hasClass('active')).toBe(true);
116
- });
125
+ expect(button.hasClass('active')).toBe(true);
126
+ });
117
127
 
118
- it(`renders value from 'text' prop`, () => {
119
- const mockText = 'mock-text';
128
+ it(`renders value from 'text' prop`, () => {
129
+ const mockText = 'mock-text';
120
130
 
121
- const wrapper = shallow(<TextButton text={mockText} />);
131
+ const wrapper = mount(<TextButton text={mockText} />);
122
132
 
123
- const button = wrapper.find('button');
133
+ const button = wrapper.find('button');
124
134
 
125
- expect(button.text()).toBe(mockText);
126
- });
135
+ expect(button.text()).toBe(mockText);
136
+ });
137
+
138
+ it(`defaults to an empty string is 'text' prop is not set`, () => {
139
+ const wrapper = mount(<TextButton />);
127
140
 
128
- it(`defaults to an empty string is 'text' prop is not set`, () => {
129
- const wrapper = shallow(<TextButton />);
141
+ const button = wrapper.find('button');
130
142
 
131
- const button = wrapper.find('button');
143
+ expect(button.text()).toBe('');
144
+ });
145
+ });
132
146
 
133
- expect(button.text()).toBe('');
147
+ describe('TextNavigationButton', () => {
148
+ it('renders the component without crashing', () => {
149
+ const wrapper = shallow(<TextButton path={'/'} />);
150
+ expect(wrapper).toBeTruthy();
151
+ });
152
+
153
+ it('renders as a navigation button when path prop is defined', () => {
154
+ const mockPath = '/home';
155
+ const wrapper = mount(
156
+ <Router>
157
+ <TextButton path={mockPath} />
158
+ </Router>,
159
+ );
160
+ const link = wrapper.find(Link);
161
+ expect(link).toHaveLength(1);
162
+ expect(link.prop('to')).toBe(mockPath);
163
+ });
164
+
165
+ it('does not open link in new tab by default', () => {
166
+ const mockPath = '/home';
167
+ const wrapper = mount(
168
+ <Router>
169
+ <TextButton path={mockPath} />
170
+ </Router>,
171
+ );
172
+ const link = wrapper.find(Link);
173
+ expect(link.prop('target')).toBeUndefined();
174
+ });
175
+
176
+ it('opens link in new tab when openInNewTab prop set to true', () => {
177
+ const mockPath = '/home';
178
+ const wrapper = mount(
179
+ <Router>
180
+ <TextButton path={mockPath} openInNewTab={true} />
181
+ </Router>,
182
+ );
183
+ const link = wrapper.find(Link);
184
+ expect(link.prop('target')).toBe('_blank');
185
+ });
186
+
187
+ it('creates a class based on the className prop', () => {
188
+ const mockClassName = 'test-class';
189
+ const wrapper = mount(
190
+ <Router>
191
+ <TextButton path={'/test'} className={mockClassName} />,
192
+ </Router>,
193
+ );
194
+ const link = wrapper.find(Link);
195
+ expect(link.hasClass(mockClassName)).toBe(true);
196
+ });
197
+
198
+ it('accepts width and height styles', () => {
199
+ const mockHeight = '75px';
200
+ const mockWidth = '80px';
201
+ const wrapper = mount(
202
+ <Router>
203
+ <TextButton path={'/test'} width={mockWidth} height={mockHeight} />
204
+ </Router>,
205
+ );
206
+ const linkStyles = wrapper
207
+ .find('Link')
208
+ .prop('style') as React.CSSProperties;
209
+
210
+ expect(linkStyles.height).toBe(mockHeight);
211
+ expect(linkStyles.width).toBe(mockWidth);
212
+ });
213
+
214
+ it('allows the link to be enabled by default', () => {
215
+ const wrapper = mount(
216
+ <Router>
217
+ <TextButton path={'/test'} />
218
+ </Router>,
219
+ );
220
+
221
+ const link = wrapper.find('Link');
222
+
223
+ expect(link.hasClass('disabled')).toBe(false);
224
+ });
225
+
226
+ it('allows the button to be disabled', () => {
227
+ const wrapper = mount(
228
+ <Router>
229
+ <TextButton path={'/test'} disabled={true} />
230
+ </Router>,
231
+ );
232
+ const link = wrapper.find('Link');
233
+ expect(link.hasClass('disabled')).toBe(true);
234
+ });
235
+
236
+ it('adds `navigation` class for link buttons', () => {
237
+ const wrapper = mount(
238
+ <Router>
239
+ <TextButton path={'/test'} />
240
+ </Router>,
241
+ );
242
+ const link = wrapper.find(Link);
243
+ expect(link.hasClass('navigation')).toBe(true);
244
+ });
134
245
  });
135
246
  });
@@ -1,4 +1,6 @@
1
1
  import { Meta, StoryObj } from '@storybook/react';
2
+ import React from 'react';
3
+ import { MemoryRouter } from 'react-router';
2
4
  import { enumToObj } from '../../../helpers/storybook';
3
5
  import { ButtonContext } from '../Button.model';
4
6
  import { TextButton } from './TextButton';
@@ -21,7 +23,25 @@ const meta: Meta<typeof TextButton> = {
21
23
  onBlur: {
22
24
  control: false,
23
25
  },
26
+ width: {
27
+ control: {
28
+ type: 'number',
29
+ },
30
+ },
31
+ height: {
32
+ control: {
33
+ type: 'number',
34
+ },
35
+ },
24
36
  },
37
+ decorators: [
38
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
39
+ (Story) => (
40
+ <MemoryRouter>
41
+ <Story />
42
+ </MemoryRouter>
43
+ ),
44
+ ],
25
45
  };
26
46
 
27
47
  export default meta;
@@ -1,24 +1,49 @@
1
1
  import clsx from 'clsx';
2
2
  import React from 'react';
3
+ import { Link } from 'react-router-dom';
3
4
  import { noop } from '../../../helpers/utils';
4
- import { BaseButtonOptions, ButtonContext } from '../Button.model';
5
+ import { ButtonContext } from '../Button.model';
6
+ import {
7
+ TextButtonProps,
8
+ TextContextButtonProps,
9
+ TextNavigationButtonProps,
10
+ } from './TextButton.model';
5
11
  import classes from './TextButton.scss';
6
12
 
7
- export interface TextButtonProps extends BaseButtonOptions {
8
- /** Button's height (default: '30px') */
9
- height?: string | number;
10
- /** Button's width (default: undefined) */
11
- width?: string | number;
12
- /** Button text */
13
- text?: string;
14
- }
15
-
16
13
  /**
17
14
  * Button which can render a text string.
18
15
  * @example
16
+ * // Rendered as button
19
17
  * <TextButton type="button" text="Click me!" />
18
+ *
19
+ * // Rendered as link
20
+ * <TextButton path="/home" text="Click me!" />
20
21
  */
21
22
  export const TextButton: React.FC<TextButtonProps> = ({
23
+ className,
24
+ dataTestId = undefined,
25
+ disabled = false,
26
+ height,
27
+ path,
28
+ text = '',
29
+ width,
30
+ openInNewTab,
31
+ ...rest
32
+ }) => {
33
+ const commonProps = { className, dataTestId, height, text, width, disabled };
34
+
35
+ return path ? (
36
+ <TextNavigationButton
37
+ {...commonProps}
38
+ path={path}
39
+ openInNewTab={openInNewTab}
40
+ />
41
+ ) : (
42
+ <TextContextButton {...commonProps} {...rest} />
43
+ );
44
+ };
45
+
46
+ const TextContextButton: React.FC<TextContextButtonProps> = ({
22
47
  type = 'button',
23
48
  height,
24
49
  width,
@@ -28,28 +53,23 @@ export const TextButton: React.FC<TextButtonProps> = ({
28
53
  className,
29
54
  dataTestId = undefined,
30
55
  onButtonClicked = noop,
31
- }: TextButtonProps) => {
32
- const customStyles = {
33
- height: height,
34
- width: width,
35
- } as React.CSSProperties;
36
-
56
+ }) => {
37
57
  return (
38
58
  <button
39
59
  className={clsx(
40
60
  classes.container,
41
- { [classes.navigation]: buttonContext === ButtonContext.Navigation },
42
61
  {
43
62
  [classes.context]: buttonContext === ButtonContext.Context,
44
- },
45
- {
46
63
  [classes.active]: buttonContext === ButtonContext.Active,
47
64
  },
48
65
  'text-button-container',
49
66
  className,
50
67
  )}
51
68
  type={type}
52
- style={customStyles}
69
+ style={{
70
+ height,
71
+ width,
72
+ }}
53
73
  onClick={onButtonClicked}
54
74
  disabled={disabled}
55
75
  data-test-id={dataTestId ?? 'text-button'}
@@ -58,3 +78,37 @@ export const TextButton: React.FC<TextButtonProps> = ({
58
78
  </button>
59
79
  );
60
80
  };
81
+
82
+ const TextNavigationButton: React.FC<TextNavigationButtonProps> = ({
83
+ height,
84
+ width,
85
+ disabled = false,
86
+ text = '',
87
+ className,
88
+ dataTestId = undefined,
89
+ path,
90
+ openInNewTab = false,
91
+ }) => {
92
+ return (
93
+ <Link
94
+ to={path}
95
+ className={clsx(
96
+ classes.container,
97
+ classes.navigation,
98
+ {
99
+ [classes.disabled]: disabled,
100
+ },
101
+ 'text-button-container',
102
+ className,
103
+ )}
104
+ style={{
105
+ height,
106
+ width,
107
+ }}
108
+ data-test-id={dataTestId ?? 'text-button'}
109
+ target={openInNewTab ? '_blank' : undefined}
110
+ >
111
+ {text}
112
+ </Link>
113
+ );
114
+ };
@@ -0,0 +1,2 @@
1
+ export { TextButton } from './TextButton';
2
+ export { TextButtonProps } from './TextButton.model';
@@ -1,7 +1,4 @@
1
+ export * from './Button';
1
2
  export { ButtonContext } from './Button.model';
2
- export { Button, ButtonProps } from './Button/Button';
3
- export {
4
- CompositeButton,
5
- CompositeButtonProps,
6
- } from './CompositeButton/CompositeButton';
7
- export { TextButton, TextButtonProps } from './TextButton/TextButton';
3
+ export * from './CompositeButton';
4
+ export * from './TextButton';
@@ -2,7 +2,7 @@ import { Data } from '../../types/data';
2
2
  import { FilterValues } from '../Filters';
3
3
  import { SortData } from '../List';
4
4
  import { ErrorType } from '../models';
5
- import { PageHeaderActionProps } from '../PageHeader/PageHeaderAction/PageHeaderAction';
5
+ import { PageHeaderJsActionProps } from '../PageHeader/PageHeaderAction/PageHeaderAction.model';
6
6
 
7
7
  /**
8
8
  * Item selection can have two modes:
@@ -24,7 +24,7 @@ interface SelectAllSelection<T extends Data> {
24
24
  }
25
25
 
26
26
  export interface ExplorerBulkAction<T extends Data>
27
- extends Omit<PageHeaderActionProps, 'onClick'> {
27
+ extends Omit<PageHeaderJsActionProps, 'onClick'> {
28
28
  /**
29
29
  * Callback to emit when a user clicks on the component
30
30
  * @param arg ItemSelection details of the bulk action
@@ -1,8 +1,8 @@
1
1
  import clsx from 'clsx';
2
2
  import { LocationDescriptor } from 'history';
3
3
  import {
4
- default as React,
5
4
  ForwardedRef,
5
+ default as React,
6
6
  useCallback,
7
7
  useEffect,
8
8
  useState,
@@ -22,9 +22,11 @@ import {
22
22
  ListSelectMode,
23
23
  SortData,
24
24
  } from '../List';
25
- import { ErrorType } from '../models';
26
25
  import { PageHeader, PageHeaderActionProps } from '../PageHeader';
26
+ import { isPageHeaderNavigationAction } from '../PageHeader/PageHeaderAction/PageHeaderAction';
27
+ import { PageHeaderJsActionProps } from '../PageHeader/PageHeaderAction/PageHeaderAction.model';
27
28
  import { getState, storeState } from '../Utils/State/GlobalState';
29
+ import { ErrorType } from '../models';
28
30
  import {
29
31
  ExplorerBulkAction,
30
32
  ExplorerDataProvider,
@@ -141,6 +143,7 @@ export interface ExplorerProps<T extends Data> {
141
143
  * The expanded state is supplied as an argument
142
144
  */
143
145
  onBulkActionsToggled?: (expanded: boolean) => void;
146
+
144
147
  /** Provide inline actions which are available through '...' context menu */
145
148
  inlineMenuActions?: (data: T) => ActionData[];
146
149
  }
@@ -339,23 +342,25 @@ export const Explorer = React.forwardRef(function Explorer<T extends Data>(
339
342
 
340
343
  const pageHeaderActionsHandler = (): PageHeaderActionProps[] => {
341
344
  return (actions ?? []).map((action) => {
342
- return {
343
- ...action,
344
- onClick: async () => {
345
- try {
346
- const result = await action.onClick();
347
- if (result) {
348
- setStationMessage(errMsg(result));
349
- }
350
- } catch (error) {
351
- setStationMessage(errMsg(error, errAction));
352
- }
353
- },
354
- };
345
+ return isPageHeaderNavigationAction(action)
346
+ ? action
347
+ : {
348
+ ...action,
349
+ onClick: async () => {
350
+ try {
351
+ const result = await action.onClick();
352
+ if (result) {
353
+ setStationMessage(errMsg(result));
354
+ }
355
+ } catch (error) {
356
+ setStationMessage(errMsg(error, errAction));
357
+ }
358
+ },
359
+ };
355
360
  });
356
361
  };
357
362
 
358
- const bulkActionsHandler = (): PageHeaderActionProps[] => {
363
+ const bulkActionsHandler = (): PageHeaderJsActionProps[] => {
359
364
  return (bulkActions ?? []).map((action) => {
360
365
  return {
361
366
  ...action,
@@ -1,3 +1,4 @@
1
+ import { LocationDescriptor } from 'history';
1
2
  import React, { ForwardedRef } from 'react';
2
3
  import { Data } from '../../../types/data';
3
4
  import { IconName } from '../../Icons';
@@ -10,10 +11,19 @@ export interface NavigationExplorerProps<T extends Data>
10
11
  ExplorerProps<T>,
11
12
  'selectionMode' | 'onBulkActionsToggled' | 'onItemClicked'
12
13
  > {
13
- /** Raised when the create action button is clicked */
14
- onCreateAction?: () => void;
14
+ /**
15
+ * - If a `LocationDescriptor` is provided, it will be treated as a path to the station to
16
+ * navigate to, and a link will be generated.
17
+ * - If a `function` is provided, it will be called when the create action is
18
+ * clicked, and a button will be generated.
19
+ */
20
+ onCreateAction?: (() => void) | LocationDescriptor;
15
21
 
16
- /** Url to navigate to when the row of data is clicked. Navigation will not occur if bulk actions is open. */
22
+ /**
23
+ * Function that calculates the URL to navigate to when a row of data is clicked.
24
+ * - The function should take the item data as input and return the URL string.
25
+ * - Navigation will not occur if bulk actions are open.
26
+ */
17
27
  calculateNavigateUrl?: (item: T) => string;
18
28
  }
19
29
 
@@ -29,30 +39,41 @@ export interface NavigationExplorerProps<T extends Data>
29
39
  * calculateNavigateUrl={(rowData => `/details/${rowData.id}`)}
30
40
  * />
31
41
  */
32
-
33
42
  export const NavigationExplorer = React.forwardRef(function NavigationExplorer<
34
43
  T extends Data,
35
44
  >(
36
- props: NavigationExplorerProps<T>,
45
+ {
46
+ onCreateAction,
47
+ calculateNavigateUrl,
48
+ actions = [],
49
+ ...rest
50
+ }: NavigationExplorerProps<T>,
37
51
  ref: ForwardedRef<ExplorerDataProviderConnection<T>>,
38
52
  ): JSX.Element {
39
- const { onCreateAction, calculateNavigateUrl, actions = [], ...rest } = props;
40
-
41
53
  return (
42
54
  <Explorer<T>
43
- ref={ref}
44
55
  {...rest}
56
+ ref={ref}
45
57
  actions={[
46
- ...(onCreateAction
58
+ ...actions,
59
+ ...(onCreateAction && typeof onCreateAction !== 'function'
60
+ ? [
61
+ {
62
+ label: 'New',
63
+ icon: IconName.Plus,
64
+ path: onCreateAction,
65
+ },
66
+ ]
67
+ : []),
68
+ ...(onCreateAction && typeof onCreateAction === 'function'
47
69
  ? [
48
- ...actions,
49
70
  {
50
71
  label: 'New',
51
72
  icon: IconName.Plus,
52
73
  onClick: onCreateAction,
53
74
  },
54
75
  ]
55
- : [...actions]),
76
+ : []),
56
77
  ]}
57
78
  selectionMode={ListSelectMode.None}
58
79
  generateItemLink={calculateNavigateUrl}