uikit-react-public 0.44.5 → 0.45.1

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 (33) hide show
  1. package/dist/components/CookiesNotice/CookieSettings.d.ts +16 -0
  2. package/dist/components/CookiesNotice/CookiesNotice.d.ts +26 -0
  3. package/dist/components/CookiesNotice/CookiesNotice.stories.d.ts +13 -0
  4. package/dist/components/CookiesNotice/__tests__/CookiesNotice.test.d.ts +1 -0
  5. package/dist/components/CookiesNotice/defaults.d.ts +6 -0
  6. package/dist/components/CookiesNotice/index.d.ts +3 -0
  7. package/dist/components/CookiesNotice/types.d.ts +12 -0
  8. package/dist/components/Paragraph/Paragraph.d.ts +1 -1
  9. package/dist/components/Text/Text.d.ts +1 -1
  10. package/dist/components/Text/__tests__/Text.test.d.ts +1 -0
  11. package/dist/components/index.d.ts +3 -0
  12. package/dist/index.js +6528 -5923
  13. package/lib/components/Button/Button.tsx +1 -1
  14. package/lib/components/Button/__tests__/__snapshots__/Button.test.tsx.snap +8 -8
  15. package/lib/components/CookiesNotice/CookieSettings.tsx +304 -0
  16. package/lib/components/CookiesNotice/CookiesNotice.stories.tsx +60 -0
  17. package/lib/components/CookiesNotice/CookiesNotice.tsx +379 -0
  18. package/lib/components/CookiesNotice/__tests__/CookiesNotice.test.tsx +180 -0
  19. package/lib/components/CookiesNotice/defaults.ts +40 -0
  20. package/lib/components/CookiesNotice/index.ts +6 -0
  21. package/lib/components/CookiesNotice/types.ts +14 -0
  22. package/lib/components/Input/Input.tsx +1 -0
  23. package/lib/components/Input/__tests__/__snapshots__/Input.test.tsx.snap +4 -4
  24. package/lib/components/Paragraph/Paragraph.tsx +13 -1
  25. package/lib/components/Paragraph/__tests__/Paragraph.test.tsx +24 -0
  26. package/lib/components/Search/__tests__/__snapshots__/Search.test.tsx.snap +2 -2
  27. package/lib/components/Text/Text.tsx +13 -0
  28. package/lib/components/Text/__tests__/Text.test.tsx +41 -0
  29. package/lib/components/Textarea/Textarea.tsx +2 -3
  30. package/lib/components/Textarea/__tests__/__snapshots__/Textarea.test.tsx.snap +4 -4
  31. package/lib/components/index.ts +9 -0
  32. package/lib/theme/common/themeCommon.ts +305 -327
  33. package/package.json +1 -1
@@ -102,7 +102,7 @@ const normalizeVariantProp = (
102
102
 
103
103
  const Button = <C extends ElementType = 'button'>({
104
104
  as,
105
- variant = 'primary',
105
+ variant = 'accent',
106
106
  destructive = false,
107
107
  size = 'medium',
108
108
  selected = false,
@@ -3,7 +3,7 @@
3
3
  exports[`Button > maps legacy destructive prop to destructive variants 1`] = `
4
4
  <button
5
5
  aria-disabled="false"
6
- class="ucl-uikit-button css-thupyi"
6
+ class="ucl-uikit-button css-ijdex1"
7
7
  data-testid="ucl-uikit-button"
8
8
  >
9
9
  Delete
@@ -13,7 +13,7 @@ exports[`Button > maps legacy destructive prop to destructive variants 1`] = `
13
13
  exports[`Button > maps legacy size default to medium 1`] = `
14
14
  <button
15
15
  aria-disabled="false"
16
- class="ucl-uikit-button css-5txlzw"
16
+ class="ucl-uikit-button css-ijdex1"
17
17
  data-testid="ucl-uikit-button"
18
18
  >
19
19
  Legacy default size
@@ -23,7 +23,7 @@ exports[`Button > maps legacy size default to medium 1`] = `
23
23
  exports[`Button > snapshot: 'as' prop with 'a' 1`] = `
24
24
  <a
25
25
  aria-disabled="false"
26
- class="ucl-uikit-button css-5txlzw"
26
+ class="ucl-uikit-button css-ijdex1"
27
27
  data-testid="ucl-uikit-button"
28
28
  href="https://ucl.ac.uk"
29
29
  >
@@ -34,7 +34,7 @@ exports[`Button > snapshot: 'as' prop with 'a' 1`] = `
34
34
  exports[`Button > snapshot: loading 1`] = `
35
35
  <button
36
36
  aria-disabled="false"
37
- class="ucl-uikit-button css-736ss2"
37
+ class="ucl-uikit-button css-1ram5ew"
38
38
  data-testid="ucl-uikit-button"
39
39
  disabled=""
40
40
  >
@@ -67,7 +67,7 @@ exports[`Button > snapshot: loading 1`] = `
67
67
  exports[`Button > snapshot: no props 1`] = `
68
68
  <button
69
69
  aria-disabled="false"
70
- class="ucl-uikit-button css-5vh71f"
70
+ class="ucl-uikit-button css-1v2bqw7"
71
71
  data-testid="ucl-uikit-button"
72
72
  />
73
73
  `;
@@ -75,7 +75,7 @@ exports[`Button > snapshot: no props 1`] = `
75
75
  exports[`Button > snapshot: testId prop 1`] = `
76
76
  <button
77
77
  aria-disabled="false"
78
- class="ucl-uikit-button css-5vh71f"
78
+ class="ucl-uikit-button css-1v2bqw7"
79
79
  data-testid="test123"
80
80
  />
81
81
  `;
@@ -91,7 +91,7 @@ exports[`Button > snapshot: variant prop 1`] = `
91
91
  exports[`Button > snapshot: with icon 1`] = `
92
92
  <button
93
93
  aria-disabled="false"
94
- class="ucl-uikit-button css-5txlzw"
94
+ class="ucl-uikit-button css-ijdex1"
95
95
  data-testid="ucl-uikit-button"
96
96
  >
97
97
  <svg
@@ -119,7 +119,7 @@ exports[`Button > snapshot: with icon 1`] = `
119
119
  exports[`Button > snapshot: with icon and iconPosition right 1`] = `
120
120
  <button
121
121
  aria-disabled="false"
122
- class="ucl-uikit-button css-5txlzw"
122
+ class="ucl-uikit-button css-ijdex1"
123
123
  data-testid="ucl-uikit-button"
124
124
  >
125
125
  Download
@@ -0,0 +1,304 @@
1
+ import {
2
+ MouseEvent as ReactMouseEvent,
3
+ MouseEventHandler,
4
+ ReactNode,
5
+ useCallback,
6
+ useId,
7
+ } from 'react';
8
+ import { css, cx } from '@emotion/css';
9
+ import useTheme from '../../theme/useTheme';
10
+ import BaseDialog, { BaseDialogProps } from '../Dialog/BaseDialog';
11
+ import Heading from '../Heading';
12
+ import Paragraph from '../Paragraph';
13
+ import Button from '../Button';
14
+ import Icon from '../Icon';
15
+ import IconButton from '../IconButton';
16
+ import Link from '../Link';
17
+ import StandaloneLink from '../StandaloneLink';
18
+ import Toggle from '../Toggle';
19
+ import {
20
+ DEFAULT_COOKIE_CATEGORIES,
21
+ DEFAULT_DESCRIPTION,
22
+ DEFAULT_DESCRIPTION_SUFFIX,
23
+ DEFAULT_POLICY_HREF,
24
+ } from './defaults';
25
+ import { CookieCategory, CookiePreferences } from './types';
26
+
27
+ const NAME = 'ucl-uikit-cookie-settings';
28
+
29
+ interface CookieSettingsProps extends Omit<
30
+ BaseDialogProps,
31
+ 'children' | 'onChange' | 'onClose' | 'size' | 'modal'
32
+ > {
33
+ cookies: CookieCategory[];
34
+ preferences: CookiePreferences;
35
+ description?: ReactNode;
36
+ policyHref?: string;
37
+ policyLabel?: ReactNode;
38
+ onClose?: (ev?: ReactMouseEvent | KeyboardEvent) => void;
39
+ onPreferenceToggle: (cookie: CookieCategory) => void;
40
+ onSavePreferences?: () => void;
41
+ saveButtonLabel?: ReactNode;
42
+ }
43
+
44
+ const CookieSettings = ({
45
+ open = false,
46
+ cookies = DEFAULT_COOKIE_CATEGORIES,
47
+ preferences,
48
+ description,
49
+ policyHref = DEFAULT_POLICY_HREF,
50
+ policyLabel = 'UCL privacy policy.',
51
+ onClose,
52
+ onPreferenceToggle,
53
+ onSavePreferences,
54
+ saveButtonLabel = 'Save preferences',
55
+ testId = NAME,
56
+ className,
57
+ ...props
58
+ }: CookieSettingsProps) => {
59
+ const [theme] = useTheme();
60
+ const headingId = useId();
61
+ const descriptionId = useId();
62
+
63
+ const handleSave = useCallback(() => {
64
+ onSavePreferences?.();
65
+ }, [onSavePreferences]);
66
+
67
+ const handleCloseClick: MouseEventHandler<HTMLButtonElement> = useCallback(
68
+ (ev) => {
69
+ onClose?.(ev);
70
+ },
71
+ [onClose]
72
+ );
73
+
74
+ const rootStyle = css`
75
+ background-color: ${theme.colour.surface.default};
76
+ `;
77
+
78
+ const headerStyle = css`
79
+ display: flex;
80
+ justify-content: space-between;
81
+ align-items: flex-start;
82
+ gap: ${theme.padding.p16};
83
+ padding: ${theme.padding.p32} ${theme.padding.p24} 0 ${theme.padding.p32};
84
+
85
+ @media (min-width: ${theme.breakpoints.tablet}px) {
86
+ padding-right: ${theme.padding.p32};
87
+ }
88
+ `;
89
+
90
+ const headingStyle = css`
91
+ padding-top: ${theme.padding.p32};
92
+ `;
93
+
94
+ const closeButtonStyle = css`
95
+ flex: 0 0 auto;
96
+ color: ${theme.colour.icon.secondary};
97
+ `;
98
+
99
+ const bodyStyle = css`
100
+ padding: ${theme.padding.p24} ${theme.padding.p32};
101
+ `;
102
+
103
+ const contentStyle = css`
104
+ display: flex;
105
+ flex-direction: column;
106
+ gap: ${theme.padding.p24};
107
+ `;
108
+
109
+ const policyLinkStyle = css`
110
+ color: ${theme.colour.text.default};
111
+
112
+ &:visited {
113
+ color: ${theme.colour.text.default};
114
+ }
115
+ `;
116
+
117
+ const listStyle = css`
118
+ display: flex;
119
+ flex-direction: column;
120
+ gap: ${theme.padding.p24};
121
+ `;
122
+
123
+ const cookieStyle = css`
124
+ display: flex;
125
+ flex-direction: column;
126
+ gap: ${theme.padding.p16};
127
+ `;
128
+
129
+ const cookieHeaderStyle = css`
130
+ display: flex;
131
+ justify-content: space-between;
132
+ align-items: flex-start;
133
+ gap: ${theme.padding.p16};
134
+ `;
135
+
136
+ const cookieTextStyle = css`
137
+ display: flex;
138
+ flex-direction: column;
139
+ gap: ${theme.padding.p16};
140
+ min-width: 0;
141
+ `;
142
+
143
+ const toggleStyle = css`
144
+ flex: 0 0 auto;
145
+ margin-top: ${theme.margin.m2};
146
+ `;
147
+
148
+ const learnMoreStyle = css`
149
+ width: fit-content;
150
+ `;
151
+
152
+ const footerStyle = css`
153
+ padding: 0 ${theme.padding.p32} ${theme.padding.p32};
154
+ display: flex;
155
+ justify-content: stretch;
156
+
157
+ @media (min-width: ${theme.breakpoints.tablet}px) {
158
+ justify-content: flex-end;
159
+ padding-top: ${theme.padding.p16};
160
+ }
161
+ `;
162
+
163
+ const saveButtonStyle = css`
164
+ width: 100%;
165
+
166
+ @media (min-width: ${theme.breakpoints.tablet}px) {
167
+ width: auto;
168
+ }
169
+ `;
170
+
171
+ const style = cx(NAME, rootStyle, className);
172
+
173
+ return (
174
+ <BaseDialog
175
+ open={open}
176
+ modal
177
+ size='large'
178
+ onClose={onClose}
179
+ className={style}
180
+ aria-labelledby={headingId}
181
+ aria-describedby={descriptionId}
182
+ testId={testId}
183
+ {...props}
184
+ >
185
+ <header className={cx(`${NAME}__header`, headerStyle)}>
186
+ <Heading
187
+ id={headingId}
188
+ level='lg'
189
+ noMargins
190
+ className={cx(`${NAME}__heading`, headingStyle)}
191
+ >
192
+ Privacy preferences
193
+ </Heading>
194
+ <IconButton
195
+ aria-label='Close cookie settings'
196
+ className={cx(`${NAME}__close-button`, closeButtonStyle)}
197
+ onClick={handleCloseClick}
198
+ >
199
+ <Icon.X
200
+ role='presentation'
201
+ aria-hidden='true'
202
+ />
203
+ </IconButton>
204
+ </header>
205
+
206
+ <div className={cx(`${NAME}__body`, bodyStyle)}>
207
+ <div className={cx(`${NAME}__content`, contentStyle)}>
208
+ {description !== undefined && (
209
+ <Paragraph
210
+ id={descriptionId}
211
+ level='md'
212
+ noMargins
213
+ >
214
+ {description}
215
+ </Paragraph>
216
+ )}
217
+ {description === undefined && (
218
+ <Paragraph
219
+ id={descriptionId}
220
+ level='md'
221
+ noMargins
222
+ >
223
+ {DEFAULT_DESCRIPTION}
224
+ <Link
225
+ href={policyHref}
226
+ className={policyLinkStyle}
227
+ noVisited
228
+ >
229
+ {policyLabel}
230
+ </Link>
231
+ {DEFAULT_DESCRIPTION_SUFFIX}
232
+ </Paragraph>
233
+ )}
234
+
235
+ <div className={cx(`${NAME}__cookies`, listStyle)}>
236
+ {cookies.map((cookie) => (
237
+ <section
238
+ key={cookie.id}
239
+ className={cx(`${NAME}__cookie`, cookieStyle)}
240
+ >
241
+ <div
242
+ className={cx(`${NAME}__cookie-header`, cookieHeaderStyle)}
243
+ >
244
+ <div className={cx(`${NAME}__cookie-text`, cookieTextStyle)}>
245
+ <Heading
246
+ level='md'
247
+ as='h3'
248
+ noMargins
249
+ >
250
+ {cookie.title}
251
+ </Heading>
252
+ <Paragraph
253
+ level='md'
254
+ noMargins
255
+ >
256
+ {cookie.description}
257
+ </Paragraph>
258
+ </div>
259
+ <Toggle
260
+ checked={preferences[cookie.id]}
261
+ disabled={cookie.required}
262
+ aria-label={
263
+ cookie.toggleLabel ??
264
+ (typeof cookie.title === 'string'
265
+ ? `Allow ${cookie.title}`
266
+ : `Allow ${cookie.id} cookies`)
267
+ }
268
+ className={cx(`${NAME}__cookie-toggle`, toggleStyle)}
269
+ onClick={() => onPreferenceToggle(cookie)}
270
+ />
271
+ </div>
272
+ {cookie.learnMoreHref && (
273
+ <StandaloneLink
274
+ href={cookie.learnMoreHref}
275
+ variant='primary'
276
+ icon={<Icon.ChevronRight />}
277
+ iconPosition='right'
278
+ noVisited
279
+ className={cx(`${NAME}__learn-more`, learnMoreStyle)}
280
+ >
281
+ {cookie.learnMoreLabel ?? 'Learn more'}
282
+ </StandaloneLink>
283
+ )}
284
+ </section>
285
+ ))}
286
+ </div>
287
+ </div>
288
+ </div>
289
+
290
+ <footer className={cx(`${NAME}__footer`, footerStyle)}>
291
+ <Button
292
+ variant='accent'
293
+ size='medium'
294
+ className={cx(`${NAME}__save-button`, saveButtonStyle)}
295
+ onClick={handleSave}
296
+ >
297
+ {saveButtonLabel}
298
+ </Button>
299
+ </footer>
300
+ </BaseDialog>
301
+ );
302
+ };
303
+
304
+ export default CookieSettings;
@@ -0,0 +1,60 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import CookiesNotice from '.';
3
+
4
+ const cookies = [
5
+ {
6
+ id: 'necessary',
7
+ title: 'Necessary Cookies',
8
+ description:
9
+ "These cookies are necessary for the website to function and can't be switched off in our systems.",
10
+ required: true,
11
+ defaultEnabled: true,
12
+ learnMoreHref: 'https://www.ucl.ac.uk/legal-services/privacy/cookie-policy',
13
+ },
14
+ {
15
+ id: 'analytics',
16
+ title: 'Analytics',
17
+ description:
18
+ 'These cookies allow us to count visits and traffic sources so we can measure and improve the performance of our site.',
19
+ defaultEnabled: true,
20
+ learnMoreHref: 'https://www.ucl.ac.uk/legal-services/privacy/cookie-policy',
21
+ },
22
+ {
23
+ id: 'marketing',
24
+ title: 'Marketing',
25
+ description:
26
+ 'These cookies are set through our site by our advertising partners. They may be used to build a profile of your interests and show you relevant ads.',
27
+ defaultEnabled: true,
28
+ learnMoreHref: 'https://www.ucl.ac.uk/legal-services/privacy/cookie-policy',
29
+ },
30
+ ];
31
+
32
+ const meta = {
33
+ title: 'Components/CookiesNotice',
34
+ component: CookiesNotice,
35
+ parameters: {
36
+ layout: 'centered',
37
+ },
38
+ tags: ['autodocs'],
39
+ } satisfies Meta<typeof CookiesNotice>;
40
+
41
+ export default meta;
42
+ type Story = StoryObj<typeof meta>;
43
+
44
+ export const NonModal: Story = {
45
+ name: 'Non-modal',
46
+ args: {
47
+ cookies,
48
+ },
49
+ };
50
+
51
+ export const Modal: Story = {
52
+ name: 'Modal',
53
+ args: {
54
+ cookies,
55
+ modal: true,
56
+ },
57
+ parameters: {
58
+ layout: 'fullscreen',
59
+ },
60
+ };