uikit-react-public 0.30.0 → 0.32.3

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 (57) hide show
  1. package/dist/components/Dialog/BaseDialog.d.ts +1 -1
  2. package/dist/components/Dialog/Dialog.d.ts +1 -1
  3. package/dist/components/Dialog/Dialog.stories.d.ts +1 -1
  4. package/dist/components/Heading/Heading.stories.d.ts +5 -7
  5. package/dist/components/MenuNew/Menu.context.d.ts +9 -0
  6. package/dist/components/MenuNew/Menu.d.ts +2 -1
  7. package/dist/components/MenuNew/MenuHeading.d.ts +1 -1
  8. package/dist/components/MenuNew/MenuItemText.d.ts +2 -1
  9. package/dist/components/MenuNew/PrimaryMenuItem.d.ts +1 -1
  10. package/dist/components/Select/Select.types.d.ts +1 -1
  11. package/dist/components/Sidebar/Sidebar.d.ts +12 -0
  12. package/dist/components/Sidebar/Sidebar.stories.d.ts +38 -0
  13. package/dist/components/Sidebar/__tests__/Sidebar.test.d.ts +1 -0
  14. package/dist/components/Sidebar/index.d.ts +2 -0
  15. package/dist/components/Snackbar/Snackbar.d.ts +9 -5
  16. package/dist/components/Snackbar/Snackbar.stories.d.ts +2 -1
  17. package/dist/components/Snackbar/Snackbars.d.ts +19 -0
  18. package/dist/components/Snackbar/Snackbars.stories.d.ts +52 -0
  19. package/dist/components/Snackbar/__tests__/Snackbars.test.d.ts +1 -0
  20. package/dist/components/Snackbar/index.d.ts +2 -0
  21. package/dist/components/index.d.ts +4 -1
  22. package/dist/index.js +5897 -5358
  23. package/dist/utils/announce.d.ts +2 -1
  24. package/lib/components/Dialog/BaseDialog.tsx +180 -161
  25. package/lib/components/Dialog/Dialog.tsx +15 -11
  26. package/lib/components/Dropdown/Dropdown.stories.tsx +69 -74
  27. package/lib/components/Heading/Documentation.mdx +4 -14
  28. package/lib/components/Heading/Heading.stories.tsx +16 -30
  29. package/lib/components/MenuNew/Menu.context.tsx +18 -0
  30. package/lib/components/MenuNew/Menu.tsx +14 -9
  31. package/lib/components/MenuNew/MenuDivider.tsx +12 -1
  32. package/lib/components/MenuNew/MenuHeading.tsx +6 -0
  33. package/lib/components/MenuNew/MenuItemText.tsx +27 -3
  34. package/lib/components/MenuNew/MenuSection.tsx +11 -0
  35. package/lib/components/MenuNew/PrimaryMenuItem.tsx +71 -2
  36. package/lib/components/Select/Select.types.ts +1 -7
  37. package/lib/components/Select/__tests__/Select.test.tsx +112 -0
  38. package/lib/components/Select/__tests__/__snapshots__/Select.test.tsx.snap +5 -0
  39. package/lib/components/Select/subcomponents/CustomSelect.tsx +75 -3
  40. package/lib/components/Select/subcomponents/FilterInput.tsx +1 -1
  41. package/lib/components/Sidebar/Sidebar.stories.tsx +94 -0
  42. package/lib/components/Sidebar/Sidebar.tsx +208 -0
  43. package/lib/components/Sidebar/__tests__/Sidebar.test.tsx +96 -0
  44. package/lib/components/Sidebar/__tests__/__snapshots__/Sidebar.test.tsx.snap +272 -0
  45. package/lib/components/Sidebar/index.ts +2 -0
  46. package/lib/components/Snackbar/Snackbar.stories.tsx +11 -0
  47. package/lib/components/Snackbar/Snackbar.tsx +162 -118
  48. package/lib/components/Snackbar/Snackbars.stories.tsx +130 -0
  49. package/lib/components/Snackbar/Snackbars.tsx +377 -0
  50. package/lib/components/Snackbar/__tests__/Snackbar.test.tsx +104 -0
  51. package/lib/components/Snackbar/__tests__/Snackbars.test.tsx +377 -0
  52. package/lib/components/Snackbar/__tests__/__snapshots__/Snackbar.test.tsx.snap +36 -19
  53. package/lib/components/Snackbar/index.ts +6 -0
  54. package/lib/components/index.ts +10 -1
  55. package/lib/utils/__tests__/announce.test.ts +53 -0
  56. package/lib/utils/announce.ts +33 -10
  57. package/package.json +1 -1
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Announces a message to screen readers.
3
+ *
3
4
  */
4
- declare const announce: (message: string, force?: boolean) => void;
5
+ declare const announce: (message: string, force?: boolean, container?: HTMLElement | null) => void;
5
6
  export declare const __resetForTesting: () => void;
6
7
  export default announce;
@@ -5,6 +5,7 @@ import React, {
5
5
  useEffect,
6
6
  useRef,
7
7
  useContext,
8
+ forwardRef,
8
9
  } from 'react';
9
10
  import { css, cx } from '@emotion/css';
10
11
  import useTheme from '../../theme/useTheme';
@@ -33,182 +34,200 @@ export interface BaseDialogProps extends HTMLAttributes<HTMLDialogElement> {
33
34
  testId?: string;
34
35
  }
35
36
 
36
- const BaseDialog = ({
37
- open = false,
38
- size = 'medium',
39
- modal = true,
40
- closeOnClickOutside = true,
41
- closeOnClickOutsideStopPropagation = true,
42
- nonModalCloseOnEscape = false,
43
- onClose,
44
- className,
45
- children,
46
- initialFocusRef,
47
- finalFocusRef,
48
- disableFocusTrap = false,
49
- restoreFocus = true,
50
- skipCloseOnInitialFocus = false,
51
- testId = NAME,
52
- ...props
53
- }: BaseDialogProps) => {
54
- const width = {
55
- small: SMALL_WIDTH,
56
- medium: MEDIUM_WIDTH,
57
- large: LARGE_WIDTH,
58
- }[size];
59
-
60
- const [theme] = useTheme();
61
-
62
- const dialogRef = useRef<HTMLDialogElement>(null);
63
- const previousActiveElement = useRef<HTMLElement | null>(null);
64
-
65
- const context = useContext(DialogContext);
66
- const dialogHeaderId = context?.dialogHeaderId;
67
- const dialogBodyId = context?.dialogBodyId;
68
-
69
- // Use the focus trap hook
70
- useFocusTrap({
71
- isActive: open && modal && !disableFocusTrap,
72
- containerRef: dialogRef,
73
- initialFocusRef,
74
- finalFocusRef,
75
- restoreFocus,
76
- skipFirstFocusable: skipCloseOnInitialFocus,
77
- });
78
-
79
- const hideBodyScroll = css`
80
- overflow: hidden;
81
- `;
82
-
83
- useEffect(() => {
84
- if (open && modal) {
85
- document.body.classList.add(hideBodyScroll);
86
- } else {
87
- document.body.classList.remove(hideBodyScroll);
88
- }
89
- return () => {
90
- document.body.classList.remove(hideBodyScroll);
91
- };
92
- }, [open, modal, hideBodyScroll]);
93
-
94
- useEffect(() => {
95
- const dialogElement = dialogRef.current;
96
-
97
- if (!dialogElement) return;
37
+ const BaseDialog = forwardRef<HTMLDialogElement, BaseDialogProps>(
38
+ function BaseDialog(
39
+ {
40
+ open = false,
41
+ size = 'medium',
42
+ modal = true,
43
+ closeOnClickOutside = true,
44
+ closeOnClickOutsideStopPropagation = true,
45
+ nonModalCloseOnEscape = false,
46
+ onClose,
47
+ className,
48
+ children,
49
+ initialFocusRef,
50
+ finalFocusRef,
51
+ disableFocusTrap = false,
52
+ restoreFocus = true,
53
+ skipCloseOnInitialFocus = false,
54
+ testId = NAME,
55
+ ...props
56
+ }: BaseDialogProps,
57
+ ref
58
+ ) {
59
+ const width = {
60
+ small: SMALL_WIDTH,
61
+ medium: MEDIUM_WIDTH,
62
+ large: LARGE_WIDTH,
63
+ }[size];
64
+
65
+ const [theme] = useTheme();
66
+
67
+ const dialogRef = useRef<HTMLDialogElement>(null);
68
+ const previousActiveElement = useRef<HTMLElement | null>(null);
69
+
70
+ const setDialogRef = useCallback(
71
+ (node: HTMLDialogElement | null) => {
72
+ dialogRef.current = node;
73
+
74
+ if (typeof ref === 'function') {
75
+ ref(node);
76
+ } else if (ref) {
77
+ ref.current = node;
78
+ }
79
+ },
80
+ [ref]
81
+ );
98
82
 
99
- if (open && !dialogElement.hasAttribute('open')) {
100
- previousActiveElement.current = document.activeElement as HTMLElement;
101
- if (modal) {
102
- dialogElement.showModal();
83
+ const context = useContext(DialogContext);
84
+ const dialogHeaderId = context?.dialogHeaderId;
85
+ const dialogBodyId = context?.dialogBodyId;
86
+
87
+ // Use the focus trap hook
88
+ useFocusTrap({
89
+ isActive: open && modal && !disableFocusTrap,
90
+ containerRef: dialogRef,
91
+ initialFocusRef,
92
+ finalFocusRef,
93
+ restoreFocus,
94
+ skipFirstFocusable: skipCloseOnInitialFocus,
95
+ });
96
+
97
+ const hideBodyScroll = css`
98
+ overflow: hidden;
99
+ `;
100
+
101
+ useEffect(() => {
102
+ if (open && modal) {
103
+ document.body.classList.add(hideBodyScroll);
103
104
  } else {
104
- dialogElement.show();
105
- }
106
- } else if (!open && dialogElement.hasAttribute('open')) {
107
- dialogElement.close();
108
- // Focus restoration is handled by the focus trap hook for modal dialogs,
109
- // but we keep the fallback for non-modal dialogs or when focus trap is disabled
110
- if ((!modal || disableFocusTrap) && restoreFocus) {
111
- previousActiveElement.current?.focus();
105
+ document.body.classList.remove(hideBodyScroll);
112
106
  }
113
- }
114
- }, [open, modal, disableFocusTrap, restoreFocus]);
115
-
116
- // Handle Escape key to close dialog
117
- useEffect(() => {
118
- if (!open || modal || !nonModalCloseOnEscape) return;
119
-
120
- const handleKeyDown = (event: KeyboardEvent) => {
121
- if (event.key === 'Escape' && onClose) {
122
- onClose(event);
107
+ return () => {
108
+ document.body.classList.remove(hideBodyScroll);
109
+ };
110
+ }, [open, modal, hideBodyScroll]);
111
+
112
+ useEffect(() => {
113
+ const dialogElement = dialogRef.current;
114
+
115
+ if (!dialogElement) return;
116
+
117
+ if (open && !dialogElement.hasAttribute('open')) {
118
+ previousActiveElement.current = document.activeElement as HTMLElement;
119
+ if (modal) {
120
+ dialogElement.showModal();
121
+ } else {
122
+ dialogElement.show();
123
+ }
124
+ } else if (!open && dialogElement.hasAttribute('open')) {
125
+ dialogElement.close();
126
+ // Focus restoration is handled by the focus trap hook for modal dialogs,
127
+ // but we keep the fallback for non-modal dialogs or when focus trap is disabled
128
+ if ((!modal || disableFocusTrap) && restoreFocus) {
129
+ previousActiveElement.current?.focus();
130
+ }
123
131
  }
124
- };
132
+ }, [open, modal, disableFocusTrap, restoreFocus]);
125
133
 
126
- document.addEventListener('keydown', handleKeyDown);
127
- return () => document.removeEventListener('keydown', handleKeyDown);
128
- }, [open, modal, nonModalCloseOnEscape, onClose]);
134
+ // Handle Escape key to close dialog
135
+ useEffect(() => {
136
+ if (!open || modal || !nonModalCloseOnEscape) return;
129
137
 
130
- const handleClick = useCallback(
131
- (ev: React.MouseEvent<HTMLDialogElement>) => {
132
- if (closeOnClickOutside && onClose && dialogRef.current) {
133
- if (closeOnClickOutsideStopPropagation) {
134
- ev.stopPropagation();
138
+ const handleKeyDown = (event: KeyboardEvent) => {
139
+ if (event.key === 'Escape' && onClose) {
140
+ onClose(event);
141
+ }
142
+ };
143
+
144
+ document.addEventListener('keydown', handleKeyDown);
145
+ return () => document.removeEventListener('keydown', handleKeyDown);
146
+ }, [open, modal, nonModalCloseOnEscape, onClose]);
147
+
148
+ const handleClick = useCallback(
149
+ (ev: React.MouseEvent<HTMLDialogElement>) => {
150
+ if (closeOnClickOutside && onClose && dialogRef.current) {
151
+ if (closeOnClickOutsideStopPropagation) {
152
+ ev.stopPropagation();
153
+ }
154
+ const rect = dialogRef.current.getBoundingClientRect();
155
+ const isInDialog =
156
+ rect.top <= ev.clientY &&
157
+ ev.clientY <= rect.top + rect.height &&
158
+ rect.left <= ev.clientX &&
159
+ ev.clientX <= rect.left + rect.width;
160
+ if (!isInDialog) {
161
+ onClose(ev);
162
+ }
135
163
  }
136
- const rect = dialogRef.current.getBoundingClientRect();
137
- const isInDialog =
138
- rect.top <= ev.clientY &&
139
- ev.clientY <= rect.top + rect.height &&
140
- rect.left <= ev.clientX &&
141
- ev.clientX <= rect.left + rect.width;
142
- if (!isInDialog) {
164
+ },
165
+ [closeOnClickOutside, closeOnClickOutsideStopPropagation, onClose]
166
+ );
167
+
168
+ const handleDialogClose = useCallback(
169
+ (ev: React.MouseEvent<HTMLDialogElement>) => {
170
+ if (onClose) {
143
171
  onClose(ev);
144
172
  }
145
- }
146
- },
147
- [closeOnClickOutside, closeOnClickOutsideStopPropagation, onClose]
148
- );
149
-
150
- const handleDialogClose = useCallback(
151
- (ev: React.MouseEvent<HTMLDialogElement>) => {
152
- if (onClose) {
153
- onClose(ev);
154
- }
155
- },
156
- [onClose]
157
- );
158
-
159
- const baseStyle = css`
160
- padding: 0;
161
- border: none;
162
- background: ${theme.color.neutral.white};
163
- color: ${theme.color.text.primary};
164
- font-family: ${theme.font.family.primary};
165
- font-size: ${theme.font.size.f16};
166
- width: 100vw;
167
- height: 100vh;
168
- margin: auto;
169
-
170
- &:modal {
171
- max-width: none;
172
- max-height: none;
173
- }
173
+ },
174
+ [onClose]
175
+ );
174
176
 
175
- @media (min-width: ${theme.breakpoints.tablet}px) {
176
- width: ${width}px;
177
- max-width: calc(100vw - ${theme.margin.m16});
178
- height: fit-content;
177
+ const baseStyle = css`
178
+ padding: 0;
179
+ border: none;
180
+ background: ${theme.color.neutral.white};
181
+ color: ${theme.color.text.primary};
182
+ font-family: ${theme.font.family.primary};
183
+ font-size: ${theme.font.size.f16};
184
+ width: 100vw;
185
+ height: 100vh;
186
+ margin: auto;
179
187
 
180
188
  &:modal {
181
- max-width: min(${width}px, calc(100vw - ${theme.margin.m16}));
182
- max-height: calc(100vh - 32px);
189
+ max-width: none;
190
+ max-height: none;
183
191
  }
184
- }
185
192
 
186
- &::backdrop {
187
- background-color: ${theme.color.overlay.blanket};
193
+ @media (min-width: ${theme.breakpoints.tablet}px) {
194
+ width: ${width}px;
195
+ max-width: calc(100vw - ${theme.margin.m16});
196
+ height: fit-content;
197
+
198
+ &:modal {
199
+ max-width: min(${width}px, calc(100vw - ${theme.margin.m16}));
200
+ max-height: calc(100vh - 32px);
201
+ }
202
+ }
203
+
204
+ &::backdrop {
205
+ background-color: ${theme.color.overlay.blanket};
206
+ }
207
+ `;
208
+
209
+ const style = cx(NAME, baseStyle, className);
210
+
211
+ if (open) {
212
+ return (
213
+ <dialog
214
+ ref={setDialogRef}
215
+ className={style}
216
+ data-testid={testId}
217
+ onClick={handleClick}
218
+ onClose={handleDialogClose}
219
+ aria-modal={modal ? 'true' : 'false'}
220
+ aria-labelledby={dialogHeaderId}
221
+ aria-describedby={dialogBodyId}
222
+ {...props}
223
+ >
224
+ {children}
225
+ </dialog>
226
+ );
227
+ } else {
228
+ return null;
188
229
  }
189
- `;
190
-
191
- const style = cx(NAME, baseStyle, className);
192
-
193
- if (open) {
194
- return (
195
- <dialog
196
- ref={dialogRef}
197
- className={style}
198
- data-testid={testId}
199
- onClick={handleClick}
200
- onClose={handleDialogClose}
201
- aria-modal={modal ? 'true' : 'false'}
202
- aria-labelledby={dialogHeaderId}
203
- aria-describedby={dialogBodyId}
204
- {...props}
205
- >
206
- {children}
207
- </dialog>
208
- );
209
- } else {
210
- return null;
211
230
  }
212
- };
231
+ );
213
232
 
214
233
  export default memo(BaseDialog);
@@ -1,4 +1,4 @@
1
- import { createContext, useId } from 'react';
1
+ import { createContext, forwardRef, useId } from 'react';
2
2
  import { css, cx } from '@emotion/css';
3
3
  import useTheme from '../../theme/useTheme';
4
4
  import BaseDialog, { BaseDialogProps } from './BaseDialog';
@@ -23,15 +23,18 @@ export interface DialogProps extends BaseDialogProps {
23
23
  onSecondaryAction?: () => void;
24
24
  }
25
25
 
26
- const Dialog = ({
27
- onClose,
28
- onAction,
29
- onSecondaryAction,
30
- testId = NAME,
31
- className,
32
- children,
33
- ...props
34
- }: DialogProps) => {
26
+ const Dialog = forwardRef<HTMLDialogElement, DialogProps>(function Dialog(
27
+ {
28
+ onClose,
29
+ onAction,
30
+ onSecondaryAction,
31
+ testId = NAME,
32
+ className,
33
+ children,
34
+ ...props
35
+ }: DialogProps,
36
+ ref
37
+ ) {
35
38
  const [theme] = useTheme();
36
39
  const dialogHeaderId = useId();
37
40
  const dialogBodyId = useId();
@@ -53,6 +56,7 @@ const Dialog = ({
53
56
  return (
54
57
  <DialogContext value={contextValue}>
55
58
  <BaseDialog
59
+ ref={ref}
56
60
  onClose={onClose}
57
61
  testId={testId}
58
62
  className={style}
@@ -63,7 +67,7 @@ const Dialog = ({
63
67
  </BaseDialog>
64
68
  </DialogContext>
65
69
  );
66
- };
70
+ });
67
71
 
68
72
  export interface DialogSubComponents {
69
73
  Header: typeof DialogHeader;
@@ -8,7 +8,6 @@ import IconButton from '../IconButton';
8
8
  import LabelledCheckbox from '../Checkbox/LabelledCheckbox';
9
9
  import useTheme from '../../theme/useTheme';
10
10
  import useMediaQuery from '../../hooks/useMediaQuery';
11
- import { ThemeContextProvider, lightTheme } from '../../theme';
12
11
 
13
12
  const meta = {
14
13
  title: 'Components/Dropdown',
@@ -74,55 +73,53 @@ export const HeaderResponsiveTrigger: Story = {
74
73
  `;
75
74
 
76
75
  return (
77
- <ThemeContextProvider initialTheme={lightTheme}>
78
- <div className={containerStyle}>
79
- <HeaderNew title='Name of the application'>
80
- <HeaderNew.MenuContainer>
81
- <Dropdown {...args}>
82
- <Dropdown.Trigger>
83
- {isDesktop ? (
84
- <Button
85
- variant='primary-subtle'
86
- size='small'
87
- icon={<Icon.Menu size={20} />}
88
- aria-label='Menu'
89
- >
90
- Menu
91
- </Button>
92
- ) : (
93
- <IconButton
94
- className={iconButtonStyle}
95
- aria-label='Menu'
96
- >
97
- <Icon.Menu size={20} />
98
- </IconButton>
99
- )}
100
- </Dropdown.Trigger>
101
- <Dropdown.Content className={menuStyle}>
102
- <button
103
- type='button'
104
- className={menuItemStyle}
76
+ <div className={containerStyle}>
77
+ <HeaderNew title='Name of the application'>
78
+ <HeaderNew.MenuContainer>
79
+ <Dropdown {...args}>
80
+ <Dropdown.Trigger>
81
+ {isDesktop ? (
82
+ <Button
83
+ variant='primary-subtle'
84
+ size='small'
85
+ icon={<Icon.Menu size={20} />}
86
+ aria-label='Menu'
105
87
  >
106
- View profile
107
- </button>
108
- <button
109
- type='button'
110
- className={menuItemStyle}
88
+ Menu
89
+ </Button>
90
+ ) : (
91
+ <IconButton
92
+ className={iconButtonStyle}
93
+ aria-label='Menu'
111
94
  >
112
- Team settings
113
- </button>
114
- <button
115
- type='button'
116
- className={menuItemStyle}
117
- >
118
- Sign out
119
- </button>
120
- </Dropdown.Content>
121
- </Dropdown>
122
- </HeaderNew.MenuContainer>
123
- </HeaderNew>
124
- </div>
125
- </ThemeContextProvider>
95
+ <Icon.Menu size={20} />
96
+ </IconButton>
97
+ )}
98
+ </Dropdown.Trigger>
99
+ <Dropdown.Content className={menuStyle}>
100
+ <button
101
+ type='button'
102
+ className={menuItemStyle}
103
+ >
104
+ View profile
105
+ </button>
106
+ <button
107
+ type='button'
108
+ className={menuItemStyle}
109
+ >
110
+ Team settings
111
+ </button>
112
+ <button
113
+ type='button'
114
+ className={menuItemStyle}
115
+ >
116
+ Sign out
117
+ </button>
118
+ </Dropdown.Content>
119
+ </Dropdown>
120
+ </HeaderNew.MenuContainer>
121
+ </HeaderNew>
122
+ </div>
126
123
  );
127
124
  },
128
125
  };
@@ -152,33 +149,31 @@ export const StandaloneInfoPanel: Story = {
152
149
  `;
153
150
 
154
151
  return (
155
- <ThemeContextProvider initialTheme={lightTheme}>
156
- <div className={containerStyle}>
157
- <Dropdown {...args}>
158
- <Dropdown.Trigger>
159
- <Button
160
- variant='secondary'
161
- size='small'
162
- icon={<Icon.Info size={20} />}
163
- >
164
- Notification preferences
165
- </Button>
166
- </Dropdown.Trigger>
167
- <Dropdown.Content
168
- className={infoContentStyle}
169
- align='left'
152
+ <div className={containerStyle}>
153
+ <Dropdown {...args}>
154
+ <Dropdown.Trigger>
155
+ <Button
156
+ variant='secondary'
157
+ size='small'
158
+ icon={<Icon.Info size={20} />}
170
159
  >
171
- <p className={infoTitleStyle}>Weekly update</p>
172
- <p className={infoBodyStyle}>
173
- Get a summary of new activity in your workspace every Friday.
174
- </p>
175
- <LabelledCheckbox id='storybook-weekly-update-checkbox'>
176
- Email me the weekly summary
177
- </LabelledCheckbox>
178
- </Dropdown.Content>
179
- </Dropdown>
180
- </div>
181
- </ThemeContextProvider>
160
+ Notification preferences
161
+ </Button>
162
+ </Dropdown.Trigger>
163
+ <Dropdown.Content
164
+ className={infoContentStyle}
165
+ align='left'
166
+ >
167
+ <p className={infoTitleStyle}>Weekly update</p>
168
+ <p className={infoBodyStyle}>
169
+ Get a summary of new activity in your workspace every Friday.
170
+ </p>
171
+ <LabelledCheckbox id='storybook-weekly-update-checkbox'>
172
+ Email me the weekly summary
173
+ </LabelledCheckbox>
174
+ </Dropdown.Content>
175
+ </Dropdown>
176
+ </div>
182
177
  );
183
178
  },
184
179
  };
@@ -15,8 +15,7 @@ export const usage = {
15
15
  level2: `<Heading level={2}>Level 2 Heading</Heading>`,
16
16
  level3: `<Heading level={3}>Level 3 Heading</Heading>`,
17
17
  level4: `<Heading level={4}>Level 4 Heading</Heading>`,
18
- withMargins: `<Heading margins={true} >Heading with margins</Heading>`,
19
- noMargins: `<Heading margins={false} >Heading with no margins</Heading>`,
18
+ withMargins: `<Heading mb="24">Heading with bottom margin</Heading>`,
20
19
  };
21
20
 
22
21
  <Meta of={HeadingStories} />
@@ -52,7 +51,8 @@ All screens in UCL Experience apps should have a sensible semantic hierarchy. Th
52
51
  ### Props
53
52
 
54
53
  - `level`: A **number** from 1 to 4, corresponding to the level of the HTML heading element to be rendered. Default is 1, if otherwise unspecified.
55
- - `margins`: A **boolean** value. If `false`, the heading will have no margins. Default is `true`.
54
+ - `m`, `mv`, `mh`, `mt`, `mb`, `ml`, `mr`: common margin props for applying theme spacing.
55
+ - `margins`: Deprecated. If `false`, the heading will have no margins.
56
56
  - `testid`: A **string** value. If provided, the heading element will have a `data-testid` attribute with this value. The <a href="https://www.educative.io/answers/what-is-the-data-testid-attribute-in-testing" target="_blank">`data-testid` attribute</a> is used for identifying this element for testing. If unspecified, `ucl-uikit-heading` is used.
57
57
 
58
58
  Pass the text you want in the heading as children: `<Heading>My heading text</Heading>`.
@@ -99,20 +99,10 @@ Custom CSS styling can be added as with all UIKit-React components by passing an
99
99
 
100
100
  Red lines are added to the examples below to show the margins of the heading element.
101
101
 
102
- Note that `margins={true}` in the below example merely specifies explicitly the default behaviour.
103
-
104
102
  <Canvas
105
- of={HeadingStories.Margins}
103
+ of={HeadingStories.withMargins}
106
104
  sourceState='shown'
107
105
  source={{
108
106
  code: usage.withMargins,
109
107
  }}
110
108
  />
111
-
112
- <Canvas
113
- of={HeadingStories.NoMargins}
114
- sourceState='shown'
115
- source={{
116
- code: usage.noMargins,
117
- }}
118
- />