@axinom/mosaic-ui 0.32.0-rc.5 → 0.32.0-rc.7

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 (67) hide show
  1. package/dist/components/Actions/Action/Action.d.ts.map +1 -1
  2. package/dist/components/Buttons/Button/Button.d.ts +9 -10
  3. package/dist/components/Buttons/Button/Button.d.ts.map +1 -1
  4. package/dist/components/Buttons/Button/Button.model.d.ts +21 -0
  5. package/dist/components/Buttons/Button/Button.model.d.ts.map +1 -0
  6. package/dist/components/Buttons/Button/index.d.ts +3 -0
  7. package/dist/components/Buttons/Button/index.d.ts.map +1 -0
  8. package/dist/components/Buttons/Button.model.d.ts +69 -7
  9. package/dist/components/Buttons/Button.model.d.ts.map +1 -1
  10. package/dist/components/Buttons/CompositeButton/CompositeButton.d.ts +5 -14
  11. package/dist/components/Buttons/CompositeButton/CompositeButton.d.ts.map +1 -1
  12. package/dist/components/Buttons/CompositeButton/CompositeButton.model.d.ts +20 -0
  13. package/dist/components/Buttons/CompositeButton/CompositeButton.model.d.ts.map +1 -0
  14. package/dist/components/Buttons/CompositeButton/index.d.ts +3 -0
  15. package/dist/components/Buttons/CompositeButton/index.d.ts.map +1 -0
  16. package/dist/components/Buttons/TextButton/TextButton.d.ts +5 -9
  17. package/dist/components/Buttons/TextButton/TextButton.d.ts.map +1 -1
  18. package/dist/components/Buttons/TextButton/TextButton.model.d.ts +18 -0
  19. package/dist/components/Buttons/TextButton/TextButton.model.d.ts.map +1 -0
  20. package/dist/components/Buttons/TextButton/index.d.ts +3 -0
  21. package/dist/components/Buttons/TextButton/index.d.ts.map +1 -0
  22. package/dist/components/Buttons/index.d.ts +3 -3
  23. package/dist/components/Buttons/index.d.ts.map +1 -1
  24. package/dist/components/Explorer/Explorer.d.ts.map +1 -1
  25. package/dist/components/Explorer/NavigationExplorer/NavigationExplorer.d.ts +3 -2
  26. package/dist/components/Explorer/NavigationExplorer/NavigationExplorer.d.ts.map +1 -1
  27. package/dist/components/FormElements/ToggleButton/ToggleButton.d.ts +2 -2
  28. package/dist/components/FormElements/ToggleButton/ToggleButton.d.ts.map +1 -1
  29. package/dist/components/List/ListRow/ListRow.d.ts.map +1 -1
  30. package/dist/components/PageHeader/PageHeaderAction/PageHeaderAction.d.ts.map +1 -1
  31. package/dist/components/models.d.ts +5 -2
  32. package/dist/components/models.d.ts.map +1 -1
  33. package/dist/index.es.js +3 -3
  34. package/dist/index.es.js.map +1 -1
  35. package/dist/index.js +3 -3
  36. package/dist/index.js.map +1 -1
  37. package/package.json +3 -3
  38. package/src/components/Actions/Action/Action.spec.tsx +14 -0
  39. package/src/components/Actions/Action/Action.tsx +5 -2
  40. package/src/components/Buttons/Button/Button.model.ts +30 -0
  41. package/src/components/Buttons/Button/Button.scss +3 -1
  42. package/src/components/Buttons/Button/Button.spec.tsx +254 -83
  43. package/src/components/Buttons/Button/Button.stories.tsx +20 -0
  44. package/src/components/Buttons/Button/Button.tsx +103 -32
  45. package/src/components/Buttons/Button/index.ts +2 -0
  46. package/src/components/Buttons/Button.model.ts +84 -9
  47. package/src/components/Buttons/CompositeButton/CompositeButton.model.ts +32 -0
  48. package/src/components/Buttons/CompositeButton/CompositeButton.scss +6 -1
  49. package/src/components/Buttons/CompositeButton/CompositeButton.spec.tsx +277 -89
  50. package/src/components/Buttons/CompositeButton/CompositeButton.stories.tsx +20 -0
  51. package/src/components/Buttons/CompositeButton/CompositeButton.tsx +94 -30
  52. package/src/components/Buttons/CompositeButton/index.ts +2 -0
  53. package/src/components/Buttons/TextButton/TextButton.model.ts +27 -0
  54. package/src/components/Buttons/TextButton/TextButton.scss +3 -1
  55. package/src/components/Buttons/TextButton/TextButton.spec.tsx +198 -87
  56. package/src/components/Buttons/TextButton/TextButton.stories.tsx +20 -0
  57. package/src/components/Buttons/TextButton/TextButton.tsx +74 -20
  58. package/src/components/Buttons/TextButton/index.ts +2 -0
  59. package/src/components/Buttons/index.ts +3 -6
  60. package/src/components/Explorer/Explorer.tsx +2 -2
  61. package/src/components/Explorer/NavigationExplorer/NavigationExplorer.tsx +4 -3
  62. package/src/components/FormElements/CustomTags/CustomTags.spec.tsx +26 -16
  63. package/src/components/FormElements/ToggleButton/ToggleButton.tsx +2 -2
  64. package/src/components/List/ListRow/ListRow.tsx +18 -13
  65. package/src/components/PageHeader/PageHeaderAction/PageHeaderAction.spec.tsx +513 -388
  66. package/src/components/PageHeader/PageHeaderAction/PageHeaderAction.tsx +11 -7
  67. package/src/components/models.ts +5 -2
@@ -1,137 +1,325 @@
1
1
  import { mount, shallow } from 'enzyme';
2
2
  import React from 'react';
3
+ import { BrowserRouter as Router } from 'react-router-dom';
4
+ import { IconName } from '../../Icons';
3
5
  import { ButtonContext } from '../Button.model';
4
6
  import { CompositeButton } from './CompositeButton';
5
7
 
6
- describe('Button', () => {
7
- it('renders the component without crashing', () => {
8
- const wrapper = shallow(<CompositeButton />);
8
+ describe('CompositeButton', () => {
9
+ describe('Composite context button', () => {
10
+ it('renders the component without crashing', () => {
11
+ const wrapper = shallow(<CompositeButton />);
9
12
 
10
- expect(wrapper).toBeTruthy();
11
- });
13
+ expect(wrapper).toBeTruthy();
14
+ });
12
15
 
13
- it('raises the onButtonClicked event', () => {
14
- const spy = jest.fn();
15
- const wrapper = shallow(<CompositeButton onButtonClicked={spy} />);
16
+ it('raises the onButtonClicked event', () => {
17
+ const spy = jest.fn();
18
+ const wrapper = mount(<CompositeButton onButtonClicked={spy} />);
16
19
 
17
- wrapper.simulate('click');
20
+ wrapper.simulate('click');
18
21
 
19
- expect(spy).toHaveBeenCalledTimes(1);
20
- });
22
+ expect(spy).toHaveBeenCalledTimes(1);
23
+ });
21
24
 
22
- it('creates a class based off of the className prop', () => {
23
- const mockClassName = 'test-class';
24
- const wrapper = shallow(<CompositeButton className={mockClassName} />);
25
+ it('creates a class based off of the className prop', () => {
26
+ const mockClassName = 'test-class';
27
+ const wrapper = mount(<CompositeButton className={mockClassName} />);
25
28
 
26
- const button = wrapper.find('button');
29
+ const button = wrapper.find('button');
27
30
 
28
- expect(button.hasClass(mockClassName)).toBe(true);
29
- });
31
+ expect(button.hasClass(mockClassName)).toBe(true);
32
+ });
30
33
 
31
- it(`button 'type' must be 'button' by default`, () => {
32
- const wrapper = shallow(<CompositeButton />);
34
+ it(`button 'type' must be 'button' by default`, () => {
35
+ const wrapper = mount(<CompositeButton />);
33
36
 
34
- const buttonType = wrapper.find('button').prop('type');
37
+ const buttonType = wrapper.find('button').prop('type');
35
38
 
36
- expect(buttonType).toBe('button');
37
- });
39
+ expect(buttonType).toBe('button');
40
+ });
38
41
 
39
- it('accepts type html attribute and width/height styles', () => {
40
- const mockType = 'submit';
41
- const mockHeight = '75px';
42
- const mockWidth = '80px';
42
+ it('accepts type html attribute and width/height styles', () => {
43
+ const mockType = 'submit';
44
+ const mockHeight = '75px';
45
+ const mockWidth = '80px';
43
46
 
44
- const wrapper = shallow(<CompositeButton />);
47
+ const wrapper = mount(<CompositeButton />);
45
48
 
46
- let buttonType = wrapper.find('button').prop('type');
47
- let buttonStyles = wrapper
48
- .find('button')
49
- .prop('style') as React.CSSProperties;
49
+ let buttonType = wrapper.find('button').prop('type');
50
+ let buttonStyles = wrapper
51
+ .find('button')
52
+ .prop('style') as React.CSSProperties;
50
53
 
51
- expect(buttonType).toBe('button');
52
- expect(buttonStyles.height).toBeUndefined();
53
- expect(buttonStyles.width).toBeUndefined();
54
+ expect(buttonType).toBe('button');
55
+ expect(buttonStyles.height).toBeUndefined();
56
+ expect(buttonStyles.width).toBeUndefined();
54
57
 
55
- wrapper.setProps({ type: mockType, height: mockHeight, width: mockWidth });
56
- wrapper.update();
58
+ wrapper.setProps({
59
+ type: mockType,
60
+ height: mockHeight,
61
+ width: mockWidth,
62
+ });
63
+ wrapper.update();
57
64
 
58
- buttonType = wrapper.find('button').prop('type');
59
- buttonStyles = wrapper.find('button').prop('style') as React.CSSProperties;
65
+ buttonType = wrapper.find('button').prop('type');
66
+ buttonStyles = wrapper
67
+ .find('button')
68
+ .prop('style') as React.CSSProperties;
60
69
 
61
- expect(buttonType).toBe(mockType);
62
- expect(buttonStyles.height).toBe(mockHeight);
63
- expect(buttonStyles.width).toBe(mockWidth);
64
- });
70
+ expect(buttonType).toBe(mockType);
71
+ expect(buttonStyles.height).toBe(mockHeight);
72
+ expect(buttonStyles.width).toBe(mockWidth);
73
+ });
65
74
 
66
- it('allows the button to be enabled by default', () => {
67
- const spy = jest.fn();
68
- const wrapper = mount(<CompositeButton onButtonClicked={spy} />);
75
+ it('allows the button to be enabled by default', () => {
76
+ const spy = jest.fn();
77
+ const wrapper = mount(<CompositeButton onButtonClicked={spy} />);
69
78
 
70
- const button = wrapper.find('button');
79
+ const button = wrapper.find('button');
71
80
 
72
- wrapper.simulate('click');
81
+ wrapper.simulate('click');
73
82
 
74
- expect(button.prop('disabled')).toBe(false);
75
- expect(spy).toHaveBeenCalledTimes(1);
76
- });
83
+ expect(button.prop('disabled')).toBe(false);
84
+ expect(spy).toHaveBeenCalledTimes(1);
85
+ });
77
86
 
78
- it('allows button to be disabled', () => {
79
- const spy = jest.fn();
80
- const wrapper = mount(
81
- <CompositeButton disabled={true} onButtonClicked={spy} />,
82
- );
87
+ it('allows button to be disabled', () => {
88
+ const spy = jest.fn();
89
+ const wrapper = mount(
90
+ <CompositeButton disabled={true} onButtonClicked={spy} />,
91
+ );
83
92
 
84
- const button = wrapper.find('button');
93
+ const button = wrapper.find('button');
85
94
 
86
- wrapper.simulate('click');
95
+ wrapper.simulate('click');
87
96
 
88
- expect(button.prop('disabled')).toBe(true);
89
- expect(spy).not.toHaveBeenCalled();
90
- });
97
+ expect(button.prop('disabled')).toBe(true);
98
+ expect(spy).not.toHaveBeenCalled();
99
+ });
91
100
 
92
- it('default should have a context class', () => {
93
- const wrapper = shallow(<CompositeButton />);
101
+ it('default should have a context class', () => {
102
+ const wrapper = mount(<CompositeButton />);
94
103
 
95
- const button = wrapper.find('button');
104
+ const button = wrapper.find('button');
96
105
 
97
- expect(button.hasClass('context')).toBe(true);
98
- });
106
+ expect(button.hasClass('context')).toBe(true);
107
+ });
99
108
 
100
- it('context button type should have a context class', () => {
101
- const wrapper = shallow(
102
- <CompositeButton buttonContext={ButtonContext.Context} />,
103
- );
109
+ it('context button type should have a context class', () => {
110
+ const wrapper = mount(
111
+ <CompositeButton buttonContext={ButtonContext.Context} />,
112
+ );
104
113
 
105
- const button = wrapper.find('button');
114
+ const button = wrapper.find('button');
106
115
 
107
- expect(button.hasClass('context')).toBe(true);
108
- });
116
+ expect(button.hasClass('context')).toBe(true);
117
+ });
109
118
 
110
- it('active button type should have a active class', () => {
111
- const wrapper = shallow(
112
- <CompositeButton buttonContext={ButtonContext.Active} />,
113
- );
119
+ it('active button type should have a active class', () => {
120
+ const wrapper = mount(
121
+ <CompositeButton buttonContext={ButtonContext.Active} />,
122
+ );
114
123
 
115
- const button = wrapper.find('button');
124
+ const button = wrapper.find('button');
116
125
 
117
- expect(button.hasClass('active')).toBe(true);
118
- });
126
+ expect(button.hasClass('active')).toBe(true);
127
+ });
119
128
 
120
- it(`renders value from 'text' prop`, () => {
121
- const mockText = 'mock-text';
129
+ it(`renders value from 'text' prop`, () => {
130
+ const mockText = 'mock-text';
122
131
 
123
- const wrapper = shallow(<CompositeButton text={mockText} />);
132
+ const wrapper = mount(<CompositeButton text={mockText} />);
124
133
 
125
- const button = wrapper.find('button');
134
+ const button = wrapper.find('button');
126
135
 
127
- expect(button.text()).toBe(mockText);
128
- });
136
+ expect(button.text()).toBe(mockText);
137
+ });
138
+
139
+ it(`defaults to an empty string is 'text' prop is not set`, () => {
140
+ const wrapper = mount(<CompositeButton />);
129
141
 
130
- it(`defaults to an empty string is 'text' prop is not set`, () => {
131
- const wrapper = shallow(<CompositeButton />);
142
+ const button = wrapper.find('button');
132
143
 
133
- const button = wrapper.find('button');
144
+ expect(button.text()).toBe('');
145
+ });
146
+ });
134
147
 
135
- expect(button.text()).toBe('');
148
+ describe('Composite navigation button', () => {
149
+ it('renders the component without crashing', () => {
150
+ const wrapper = shallow(<CompositeButton path="/example" />);
151
+
152
+ expect(wrapper).toBeTruthy();
153
+ });
154
+
155
+ it('renders a Link component with the provided path', () => {
156
+ const mockPath = '/example';
157
+ const wrapper = mount(
158
+ <Router>
159
+ <CompositeButton path={mockPath} />
160
+ </Router>,
161
+ );
162
+
163
+ const link = wrapper.find('Link');
164
+
165
+ expect(link.prop('to')).toBe(mockPath);
166
+ });
167
+
168
+ it('applies the provided className to the component', () => {
169
+ const mockClassName = 'test-class';
170
+ const wrapper = mount(
171
+ <Router>
172
+ <CompositeButton className={mockClassName} path="/example" />
173
+ </Router>,
174
+ );
175
+
176
+ const link = wrapper.find('Link');
177
+
178
+ expect(link.hasClass(mockClassName)).toBe(true);
179
+ });
180
+
181
+ it('applies the disabled class when disabled prop is true', () => {
182
+ const wrapper = mount(
183
+ <Router>
184
+ <CompositeButton disabled={true} path="/example" />,
185
+ </Router>,
186
+ );
187
+
188
+ const link = wrapper.find('Link');
189
+
190
+ expect(link.hasClass('disabled')).toBe(true);
191
+ });
192
+
193
+ it('does not apply the disabled class when disabled prop is false', () => {
194
+ const wrapper = mount(
195
+ <Router>
196
+ <CompositeButton disabled={false} path="/example" />
197
+ </Router>,
198
+ );
199
+
200
+ const link = wrapper.find('Link');
201
+
202
+ expect(link.hasClass('disabled')).toBe(false);
203
+ });
204
+
205
+ it('sets the height and width styles based on the provided props', () => {
206
+ const mockHeight = '50px';
207
+ const mockWidth = '100px';
208
+ const wrapper = mount(
209
+ <Router>
210
+ <CompositeButton
211
+ height={mockHeight}
212
+ width={mockWidth}
213
+ path="/example"
214
+ />
215
+ ,
216
+ </Router>,
217
+ );
218
+
219
+ const link = wrapper.find('Link');
220
+
221
+ expect(link.prop('style')?.height).toBe(mockHeight);
222
+ expect(link.prop('style')?.width).toBe(mockWidth);
223
+ });
224
+
225
+ it('sets the direction style to "rtl" when iconPosition is "left"', () => {
226
+ const wrapper = mount(
227
+ <Router>
228
+ <CompositeButton iconPosition="left" path="/example" />
229
+ </Router>,
230
+ );
231
+
232
+ const link = wrapper.find('Link');
233
+
234
+ expect(link.prop('style')).toEqual({
235
+ direction: 'rtl',
236
+ });
237
+ });
238
+
239
+ it('sets the direction style to "ltr" when iconPosition is "right"', () => {
240
+ const wrapper = mount(
241
+ <Router>
242
+ <CompositeButton iconPosition="right" path="/example" />
243
+ </Router>,
244
+ );
245
+
246
+ const link = wrapper.find('Link');
247
+
248
+ expect(link.prop('style')).toEqual({
249
+ direction: 'ltr',
250
+ });
251
+ });
252
+
253
+ it('renders the text inside a div', () => {
254
+ const mockText = 'Example Button';
255
+ const wrapper = mount(
256
+ <Router>
257
+ <CompositeButton text={mockText} path="/example" />
258
+ </Router>,
259
+ );
260
+
261
+ const textDiv = wrapper.find('div').at(0);
262
+
263
+ expect(textDiv.text()).toBe(mockText);
264
+ });
265
+
266
+ it('renders the icon inside a div when icon prop is provided', () => {
267
+ const mockIcon = IconName.Calendar;
268
+ const wrapper = mount(
269
+ <Router>
270
+ <CompositeButton icon={mockIcon} path="/example" />
271
+ </Router>,
272
+ );
273
+
274
+ const iconDiv = wrapper.find('div').at(1);
275
+
276
+ expect(iconDiv.find('Icons').prop('icon')).toBe(mockIcon);
277
+ });
278
+
279
+ it('renders the Icons component with the correct default icon', () => {
280
+ const wrapper = mount(
281
+ <Router>
282
+ <CompositeButton path="/example" />
283
+ </Router>,
284
+ );
285
+ const iconDiv = wrapper.find('div').at(1);
286
+
287
+ expect(iconDiv.find('Icons').prop('icon')).toBe(IconName.ChevronRight);
288
+ });
289
+
290
+ it('renders `External` icon by default when none provided and `openInNewTab: true`', () => {
291
+ const wrapper = mount(
292
+ <Router>
293
+ <CompositeButton path="/example" openInNewTab={true} />
294
+ </Router>,
295
+ );
296
+ const iconDiv = wrapper.find('div').at(1);
297
+
298
+ expect(iconDiv.find('Icons').prop('icon')).toBe(IconName.External);
299
+ });
300
+
301
+ it('renders the default data-test-id when dataTestId prop is not provided', () => {
302
+ const wrapper = mount(
303
+ <Router>
304
+ <CompositeButton path="/example" />
305
+ </Router>,
306
+ );
307
+
308
+ const link = wrapper.find('Link');
309
+
310
+ expect(link.prop('data-test-id')).toBe('text-button');
311
+ });
312
+
313
+ it('renders the provided data-test-id when dataTestId prop is provided', () => {
314
+ const mockDataTestId = 'test-id';
315
+ const wrapper = mount(
316
+ <Router>
317
+ <CompositeButton dataTestId={mockDataTestId} path="/example" />
318
+ </Router>,
319
+ );
320
+ const link = wrapper.find('Link');
321
+
322
+ expect(link.prop('data-test-id')).toBe(mockDataTestId);
323
+ });
136
324
  });
137
325
  });
@@ -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 { IconName } from '../../Icons';
4
6
  import { ButtonContext } from '../Button.model';
@@ -29,7 +31,25 @@ const meta: Meta<typeof CompositeButton> = {
29
31
  onBlur: {
30
32
  control: false,
31
33
  },
34
+ width: {
35
+ control: {
36
+ type: 'number',
37
+ },
38
+ },
39
+ height: {
40
+ control: {
41
+ type: 'number',
42
+ },
43
+ },
32
44
  },
45
+ decorators: [
46
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
47
+ (Story) => (
48
+ <MemoryRouter>
49
+ <Story />
50
+ </MemoryRouter>
51
+ ),
52
+ ],
33
53
  };
34
54
 
35
55
  export default meta;
@@ -1,64 +1,89 @@
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
5
  import { IconName, Icons } from '../../Icons';
5
- import { BaseButtonOptions, ButtonContext } from '../Button.model';
6
+ import { ButtonContext } from '../Button.model';
7
+ import {
8
+ CompositeButtonProps,
9
+ CompositeJsButtonProps,
10
+ CompositeNavigationButtonProps,
11
+ } from './CompositeButton.model';
6
12
  import classes from './CompositeButton.scss';
7
13
 
8
- // TODO: Refactor buttons. Use a composition model.
9
- export interface CompositeButtonProps extends BaseButtonOptions {
10
- /** Button's height (default: '30px') */
11
- height?: string | number;
12
- /** Button's width (default: undefined) */
13
- width?: string | number;
14
- /** Button text */
15
- text?: string;
16
- /** Optional icon */
17
- icon?: IconName;
18
- /** Optional Icon Position */
19
- iconPosition?: 'left' | 'right';
20
- }
21
-
22
14
  /**
23
15
  * Button which can render an text together with an icon.
24
16
  * @example
17
+ * // Rendered as button
25
18
  * <CompositeButton type="button" text="Click me!" />
19
+ *
20
+ * // Rendered as link
21
+ * <CompositeButton path="/home" text="Click me!" />
26
22
  */
27
23
  export const CompositeButton: React.FC<CompositeButtonProps> = ({
28
- type = 'button',
24
+ className,
25
+ dataTestId,
26
+ disabled,
29
27
  height,
28
+ icon,
29
+ iconPosition,
30
+ path,
31
+ text = '',
30
32
  width,
33
+ openInNewTab,
34
+ ...rest
35
+ }) => {
36
+ const commonProps = {
37
+ className,
38
+ dataTestId,
39
+ disabled,
40
+ height,
41
+ icon,
42
+ iconPosition,
43
+ text,
44
+ width,
45
+ };
46
+ return path ? (
47
+ <CompositeNavigationButton
48
+ {...commonProps}
49
+ path={path}
50
+ openInNewTab={openInNewTab}
51
+ />
52
+ ) : (
53
+ <CompositeContextButton {...commonProps} {...rest} />
54
+ );
55
+ };
56
+
57
+ const CompositeContextButton: React.FC<CompositeJsButtonProps> = ({
31
58
  buttonContext = ButtonContext.Context,
59
+ className,
60
+ dataTestId = undefined,
32
61
  disabled = false,
33
- text = '',
62
+ height,
34
63
  icon,
35
64
  iconPosition,
36
- className,
37
- dataTestId = undefined,
65
+ text = '',
66
+ type = 'button',
67
+ width,
38
68
  onButtonClicked = noop,
39
- }: CompositeButtonProps) => {
40
- const customStyles = {
41
- height: height,
42
- width: width,
43
- direction: iconPosition === 'left' ? 'rtl' : 'ltr',
44
- } as React.CSSProperties;
45
-
69
+ }) => {
46
70
  return (
47
71
  <button
48
72
  className={clsx(
49
73
  classes.container,
50
- { [classes.navigation]: buttonContext === ButtonContext.Navigation },
51
74
  {
52
75
  [classes.context]: buttonContext === ButtonContext.Context,
53
- },
54
- {
55
76
  [classes.active]: buttonContext === ButtonContext.Active,
56
77
  },
57
78
  'composite-button-container',
58
79
  className,
59
80
  )}
60
81
  type={type}
61
- style={customStyles}
82
+ style={{
83
+ height,
84
+ width,
85
+ direction: iconPosition === 'left' ? 'rtl' : 'ltr',
86
+ }}
62
87
  onClick={onButtonClicked}
63
88
  disabled={disabled}
64
89
  data-test-id={dataTestId ?? 'text-button'}
@@ -70,3 +95,42 @@ export const CompositeButton: React.FC<CompositeButtonProps> = ({
70
95
  </button>
71
96
  );
72
97
  };
98
+
99
+ const CompositeNavigationButton: React.FC<CompositeNavigationButtonProps> = ({
100
+ className,
101
+ dataTestId = undefined,
102
+ disabled = false,
103
+ height,
104
+ icon,
105
+ iconPosition,
106
+ path,
107
+ text = '',
108
+ width,
109
+ openInNewTab,
110
+ }) => {
111
+ const defaultIcon = openInNewTab ? IconName.External : IconName.ChevronRight;
112
+
113
+ return (
114
+ <Link
115
+ to={path}
116
+ className={clsx(
117
+ classes.container,
118
+ classes.navigation,
119
+ { [classes.disabled]: disabled },
120
+ 'composite-button-container',
121
+ className,
122
+ )}
123
+ style={{
124
+ height,
125
+ width,
126
+ direction: iconPosition === 'left' ? 'rtl' : 'ltr',
127
+ }}
128
+ data-test-id={dataTestId ?? 'text-button'}
129
+ >
130
+ <div>{text}</div>
131
+ <div className={classes.icon}>
132
+ <Icons icon={icon ? icon : defaultIcon} />
133
+ </div>
134
+ </Link>
135
+ );
136
+ };
@@ -0,0 +1,2 @@
1
+ export { CompositeButton } from './CompositeButton';
2
+ export { CompositeButtonProps } from './CompositeButton.model';
@@ -0,0 +1,27 @@
1
+ import {
2
+ CommonJsButtonOptions,
3
+ CommonNavigationButtonOptions,
4
+ TextButtonOptions,
5
+ } from '../Button.model';
6
+
7
+ export type TextButtonProps =
8
+ | (TextNavigationButtonProps | TextContextButtonProps) & {
9
+ /** Optional button's height (default: 30px) */
10
+ height?: string | number;
11
+ /** Optional button's width (default: undefined) */
12
+ width?: string | number;
13
+ };
14
+
15
+ /**
16
+ * Button options for text buttons with navigation
17
+ */
18
+ export interface TextNavigationButtonProps
19
+ extends CommonNavigationButtonOptions,
20
+ TextButtonOptions {}
21
+
22
+ /**
23
+ * Button options for text buttons with JS handlers
24
+ */
25
+ export interface TextContextButtonProps
26
+ extends CommonJsButtonOptions,
27
+ TextButtonOptions {}
@@ -21,6 +21,7 @@
21
21
  &.navigation {
22
22
  color: white;
23
23
  background-color: $light-blue;
24
+ width: fit-content;
24
25
 
25
26
  &:hover {
26
27
  background-color: lighten($light-blue, 10%);
@@ -31,8 +32,9 @@
31
32
  color: $light-blue;
32
33
  }
33
34
 
34
- &:disabled {
35
+ &.disabled {
35
36
  background-color: $gray;
37
+ pointer-events: none;
36
38
  color: rgba(white, 0.5);
37
39
  }
38
40
  }