@axinom/mosaic-ui 0.32.0-rc.4 → 0.32.0-rc.6
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/components/Actions/Action/Action.d.ts.map +1 -1
- package/dist/components/Buttons/Button/Button.d.ts +9 -10
- package/dist/components/Buttons/Button/Button.d.ts.map +1 -1
- package/dist/components/Buttons/Button/Button.model.d.ts +21 -0
- package/dist/components/Buttons/Button/Button.model.d.ts.map +1 -0
- package/dist/components/Buttons/Button/index.d.ts +3 -0
- package/dist/components/Buttons/Button/index.d.ts.map +1 -0
- package/dist/components/Buttons/Button.model.d.ts +69 -7
- package/dist/components/Buttons/Button.model.d.ts.map +1 -1
- package/dist/components/Buttons/CompositeButton/CompositeButton.d.ts +5 -14
- package/dist/components/Buttons/CompositeButton/CompositeButton.d.ts.map +1 -1
- package/dist/components/Buttons/CompositeButton/CompositeButton.model.d.ts +20 -0
- package/dist/components/Buttons/CompositeButton/CompositeButton.model.d.ts.map +1 -0
- package/dist/components/Buttons/CompositeButton/index.d.ts +3 -0
- package/dist/components/Buttons/CompositeButton/index.d.ts.map +1 -0
- package/dist/components/Buttons/TextButton/TextButton.d.ts +5 -9
- package/dist/components/Buttons/TextButton/TextButton.d.ts.map +1 -1
- package/dist/components/Buttons/TextButton/TextButton.model.d.ts +18 -0
- package/dist/components/Buttons/TextButton/TextButton.model.d.ts.map +1 -0
- package/dist/components/Buttons/TextButton/index.d.ts +3 -0
- package/dist/components/Buttons/TextButton/index.d.ts.map +1 -0
- package/dist/components/Buttons/index.d.ts +3 -3
- package/dist/components/Buttons/index.d.ts.map +1 -1
- package/dist/components/Explorer/Explorer.d.ts.map +1 -1
- package/dist/components/Explorer/NavigationExplorer/NavigationExplorer.d.ts +3 -2
- package/dist/components/Explorer/NavigationExplorer/NavigationExplorer.d.ts.map +1 -1
- package/dist/components/FormElements/ToggleButton/ToggleButton.d.ts +2 -2
- package/dist/components/FormElements/ToggleButton/ToggleButton.d.ts.map +1 -1
- package/dist/components/List/ListRow/ListRow.d.ts.map +1 -1
- package/dist/components/PageHeader/PageHeaderAction/PageHeaderAction.d.ts.map +1 -1
- package/dist/components/models.d.ts +5 -2
- package/dist/components/models.d.ts.map +1 -1
- package/dist/index.es.js +3 -3
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/components/Actions/Action/Action.spec.tsx +14 -0
- package/src/components/Actions/Action/Action.tsx +5 -2
- package/src/components/Buttons/Button/Button.model.ts +30 -0
- package/src/components/Buttons/Button/Button.scss +3 -1
- package/src/components/Buttons/Button/Button.spec.tsx +254 -83
- package/src/components/Buttons/Button/Button.stories.tsx +20 -0
- package/src/components/Buttons/Button/Button.tsx +103 -32
- package/src/components/Buttons/Button/index.ts +2 -0
- package/src/components/Buttons/Button.model.ts +84 -9
- package/src/components/Buttons/CompositeButton/CompositeButton.model.ts +32 -0
- package/src/components/Buttons/CompositeButton/CompositeButton.scss +6 -1
- package/src/components/Buttons/CompositeButton/CompositeButton.spec.tsx +277 -89
- package/src/components/Buttons/CompositeButton/CompositeButton.stories.tsx +20 -0
- package/src/components/Buttons/CompositeButton/CompositeButton.tsx +94 -30
- package/src/components/Buttons/CompositeButton/index.ts +2 -0
- package/src/components/Buttons/TextButton/TextButton.model.ts +27 -0
- package/src/components/Buttons/TextButton/TextButton.scss +3 -1
- package/src/components/Buttons/TextButton/TextButton.spec.tsx +198 -87
- package/src/components/Buttons/TextButton/TextButton.stories.tsx +20 -0
- package/src/components/Buttons/TextButton/TextButton.tsx +74 -20
- package/src/components/Buttons/TextButton/index.ts +2 -0
- package/src/components/Buttons/index.ts +3 -6
- package/src/components/Explorer/Explorer.tsx +2 -2
- package/src/components/Explorer/NavigationExplorer/NavigationExplorer.tsx +4 -3
- package/src/components/FormElements/CustomTags/CustomTags.spec.tsx +26 -16
- package/src/components/FormElements/ToggleButton/ToggleButton.tsx +2 -2
- package/src/components/List/ListRow/ListRow.tsx +18 -13
- package/src/components/PageHeader/PageHeaderAction/PageHeaderAction.spec.tsx +513 -388
- package/src/components/PageHeader/PageHeaderAction/PageHeaderAction.tsx +11 -7
- 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('
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
describe('CompositeButton', () => {
|
|
9
|
+
describe('Composite context button', () => {
|
|
10
|
+
it('renders the component without crashing', () => {
|
|
11
|
+
const wrapper = shallow(<CompositeButton />);
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
expect(wrapper).toBeTruthy();
|
|
14
|
+
});
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
it('raises the onButtonClicked event', () => {
|
|
17
|
+
const spy = jest.fn();
|
|
18
|
+
const wrapper = mount(<CompositeButton onButtonClicked={spy} />);
|
|
16
19
|
|
|
17
|
-
|
|
20
|
+
wrapper.simulate('click');
|
|
18
21
|
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
23
|
+
});
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
29
|
+
const button = wrapper.find('button');
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
expect(button.hasClass(mockClassName)).toBe(true);
|
|
32
|
+
});
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
it(`button 'type' must be 'button' by default`, () => {
|
|
35
|
+
const wrapper = mount(<CompositeButton />);
|
|
33
36
|
|
|
34
|
-
|
|
37
|
+
const buttonType = wrapper.find('button').prop('type');
|
|
35
38
|
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
expect(buttonType).toBe('button');
|
|
40
|
+
});
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
47
|
+
const wrapper = mount(<CompositeButton />);
|
|
45
48
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
let buttonType = wrapper.find('button').prop('type');
|
|
50
|
+
let buttonStyles = wrapper
|
|
51
|
+
.find('button')
|
|
52
|
+
.prop('style') as React.CSSProperties;
|
|
50
53
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
expect(buttonType).toBe('button');
|
|
55
|
+
expect(buttonStyles.height).toBeUndefined();
|
|
56
|
+
expect(buttonStyles.width).toBeUndefined();
|
|
54
57
|
|
|
55
|
-
|
|
56
|
-
|
|
58
|
+
wrapper.setProps({
|
|
59
|
+
type: mockType,
|
|
60
|
+
height: mockHeight,
|
|
61
|
+
width: mockWidth,
|
|
62
|
+
});
|
|
63
|
+
wrapper.update();
|
|
57
64
|
|
|
58
|
-
|
|
59
|
-
|
|
65
|
+
buttonType = wrapper.find('button').prop('type');
|
|
66
|
+
buttonStyles = wrapper
|
|
67
|
+
.find('button')
|
|
68
|
+
.prop('style') as React.CSSProperties;
|
|
60
69
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
70
|
+
expect(buttonType).toBe(mockType);
|
|
71
|
+
expect(buttonStyles.height).toBe(mockHeight);
|
|
72
|
+
expect(buttonStyles.width).toBe(mockWidth);
|
|
73
|
+
});
|
|
65
74
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
79
|
+
const button = wrapper.find('button');
|
|
71
80
|
|
|
72
|
-
|
|
81
|
+
wrapper.simulate('click');
|
|
73
82
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
83
|
+
expect(button.prop('disabled')).toBe(false);
|
|
84
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
85
|
+
});
|
|
77
86
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
93
|
+
const button = wrapper.find('button');
|
|
85
94
|
|
|
86
|
-
|
|
95
|
+
wrapper.simulate('click');
|
|
87
96
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
97
|
+
expect(button.prop('disabled')).toBe(true);
|
|
98
|
+
expect(spy).not.toHaveBeenCalled();
|
|
99
|
+
});
|
|
91
100
|
|
|
92
|
-
|
|
93
|
-
|
|
101
|
+
it('default should have a context class', () => {
|
|
102
|
+
const wrapper = mount(<CompositeButton />);
|
|
94
103
|
|
|
95
|
-
|
|
104
|
+
const button = wrapper.find('button');
|
|
96
105
|
|
|
97
|
-
|
|
98
|
-
|
|
106
|
+
expect(button.hasClass('context')).toBe(true);
|
|
107
|
+
});
|
|
99
108
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
109
|
+
it('context button type should have a context class', () => {
|
|
110
|
+
const wrapper = mount(
|
|
111
|
+
<CompositeButton buttonContext={ButtonContext.Context} />,
|
|
112
|
+
);
|
|
104
113
|
|
|
105
|
-
|
|
114
|
+
const button = wrapper.find('button');
|
|
106
115
|
|
|
107
|
-
|
|
108
|
-
|
|
116
|
+
expect(button.hasClass('context')).toBe(true);
|
|
117
|
+
});
|
|
109
118
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
119
|
+
it('active button type should have a active class', () => {
|
|
120
|
+
const wrapper = mount(
|
|
121
|
+
<CompositeButton buttonContext={ButtonContext.Active} />,
|
|
122
|
+
);
|
|
114
123
|
|
|
115
|
-
|
|
124
|
+
const button = wrapper.find('button');
|
|
116
125
|
|
|
117
|
-
|
|
118
|
-
|
|
126
|
+
expect(button.hasClass('active')).toBe(true);
|
|
127
|
+
});
|
|
119
128
|
|
|
120
|
-
|
|
121
|
-
|
|
129
|
+
it(`renders value from 'text' prop`, () => {
|
|
130
|
+
const mockText = 'mock-text';
|
|
122
131
|
|
|
123
|
-
|
|
132
|
+
const wrapper = mount(<CompositeButton text={mockText} />);
|
|
124
133
|
|
|
125
|
-
|
|
134
|
+
const button = wrapper.find('button');
|
|
126
135
|
|
|
127
|
-
|
|
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
|
-
|
|
131
|
-
const wrapper = shallow(<CompositeButton />);
|
|
142
|
+
const button = wrapper.find('button');
|
|
132
143
|
|
|
133
|
-
|
|
144
|
+
expect(button.text()).toBe('');
|
|
145
|
+
});
|
|
146
|
+
});
|
|
134
147
|
|
|
135
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
62
|
+
height,
|
|
34
63
|
icon,
|
|
35
64
|
iconPosition,
|
|
36
|
-
|
|
37
|
-
|
|
65
|
+
text = '',
|
|
66
|
+
type = 'button',
|
|
67
|
+
width,
|
|
38
68
|
onButtonClicked = noop,
|
|
39
|
-
}
|
|
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={
|
|
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,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
|
-
|
|
35
|
+
&.disabled {
|
|
35
36
|
background-color: $gray;
|
|
37
|
+
pointer-events: none;
|
|
36
38
|
color: rgba(white, 0.5);
|
|
37
39
|
}
|
|
38
40
|
}
|