@transferwise/components 0.0.0-experimental-b26a3c1 → 0.0.0-experimental-f2eec9e

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 (77) hide show
  1. package/build/avatarLayout/AvatarLayout.js +2 -1
  2. package/build/avatarLayout/AvatarLayout.js.map +1 -1
  3. package/build/avatarLayout/AvatarLayout.mjs +2 -1
  4. package/build/avatarLayout/AvatarLayout.mjs.map +1 -1
  5. package/build/button/Button.js +9 -13
  6. package/build/button/Button.js.map +1 -1
  7. package/build/button/Button.mjs +9 -13
  8. package/build/button/Button.mjs.map +1 -1
  9. package/build/index.js +2 -0
  10. package/build/index.js.map +1 -1
  11. package/build/index.mjs +1 -0
  12. package/build/index.mjs.map +1 -1
  13. package/build/main.css +13 -1
  14. package/build/nudge/Nudge.js.map +1 -1
  15. package/build/nudge/Nudge.mjs.map +1 -1
  16. package/build/styles/avatarLayout/AvatarLayout.css +2 -1
  17. package/build/styles/main.css +13 -1
  18. package/build/styles/nudge/Nudge.css +11 -0
  19. package/build/table/Table.js +166 -0
  20. package/build/table/Table.js.map +1 -0
  21. package/build/table/Table.messages.js +24 -0
  22. package/build/table/Table.messages.js.map +1 -0
  23. package/build/table/Table.messages.mjs +22 -0
  24. package/build/table/Table.messages.mjs.map +1 -0
  25. package/build/table/Table.mjs +164 -0
  26. package/build/table/Table.mjs.map +1 -0
  27. package/build/table/TableCell.js +86 -0
  28. package/build/table/TableCell.js.map +1 -0
  29. package/build/table/TableCell.mjs +84 -0
  30. package/build/table/TableCell.mjs.map +1 -0
  31. package/build/table/TableHeader.js +57 -0
  32. package/build/table/TableHeader.js.map +1 -0
  33. package/build/table/TableHeader.mjs +55 -0
  34. package/build/table/TableHeader.mjs.map +1 -0
  35. package/build/table/TableRow.js +85 -0
  36. package/build/table/TableRow.js.map +1 -0
  37. package/build/table/TableRow.mjs +83 -0
  38. package/build/table/TableRow.mjs.map +1 -0
  39. package/build/table/TableStatusText.js +54 -0
  40. package/build/table/TableStatusText.js.map +1 -0
  41. package/build/table/TableStatusText.mjs +52 -0
  42. package/build/table/TableStatusText.mjs.map +1 -0
  43. package/build/types/avatarLayout/AvatarLayout.d.ts.map +1 -1
  44. package/build/types/button/Button.d.ts.map +1 -1
  45. package/build/types/button/Button.types.d.ts +6 -15
  46. package/build/types/button/Button.types.d.ts.map +1 -1
  47. package/build/types/index.d.ts +2 -0
  48. package/build/types/index.d.ts.map +1 -1
  49. package/build/types/nudge/Nudge.d.ts +1 -1
  50. package/build/types/nudge/Nudge.d.ts.map +1 -1
  51. package/build/types/uploadInput/uploadButton/getAllowedFileTypes.d.ts.map +1 -1
  52. package/build/uploadInput/uploadButton/getAllowedFileTypes.js +23 -3
  53. package/build/uploadInput/uploadButton/getAllowedFileTypes.js.map +1 -1
  54. package/build/uploadInput/uploadButton/getAllowedFileTypes.mjs +23 -3
  55. package/build/uploadInput/uploadButton/getAllowedFileTypes.mjs.map +1 -1
  56. package/package.json +3 -3
  57. package/src/avatarLayout/AvatarLayout.css +2 -1
  58. package/src/avatarLayout/AvatarLayout.less +1 -1
  59. package/src/avatarLayout/AvatarLayout.tsx +1 -0
  60. package/src/button/Button.accessibility.docs.mdx +103 -0
  61. package/src/button/Button.story.tsx +145 -170
  62. package/src/button/Button.tsx +9 -16
  63. package/src/button/Button.types.ts +9 -15
  64. package/src/button/LegacyButton.story.tsx +7 -2
  65. package/src/drawer/Drawer.spec.tsx +93 -0
  66. package/src/index.ts +12 -0
  67. package/src/main.css +13 -1
  68. package/src/nudge/Nudge.css +11 -0
  69. package/src/nudge/Nudge.less +3 -0
  70. package/src/nudge/Nudge.story.tsx +10 -0
  71. package/src/nudge/Nudge.tsx +2 -1
  72. package/src/uploadInput/UploadInput.tests.story.tsx +5 -5
  73. package/src/uploadInput/uploadButton/getAllowedFileTypes.spec.ts +12 -0
  74. package/src/uploadInput/uploadButton/getAllowedFileTypes.ts +33 -7
  75. package/src/drawer/Drawer.rtl.spec.tsx +0 -59
  76. package/src/drawer/Drawer.spec.js +0 -101
  77. package/src/drawer/__snapshots__/Drawer.rtl.spec.tsx.snap +0 -55
@@ -0,0 +1,93 @@
1
+ import { render, screen, userEvent, mockMatchMedia } from '../test-utils';
2
+ import { Position } from '../common';
3
+
4
+ import Drawer from './Drawer';
5
+
6
+ jest.mock('../common/DOMOperations');
7
+ mockMatchMedia();
8
+
9
+ describe('Drawer', () => {
10
+ const renderedComponent = (props: React.ComponentProps<typeof Drawer>) =>
11
+ render(<Drawer {...props} />);
12
+
13
+ const props = {
14
+ open: true,
15
+ onClose: jest.fn(),
16
+ headerTitle: 'Drawer Title',
17
+ footerContent: <div>Footer Content</div>,
18
+ children: <p>Drawer Content</p>,
19
+ className: 'drawer-class',
20
+ };
21
+
22
+ beforeEach(() => {
23
+ jest.clearAllMocks();
24
+ });
25
+
26
+ it('renders the Drawer component', () => {
27
+ renderedComponent(props);
28
+ expect(screen.getByRole('dialog')).toBeInTheDocument();
29
+ });
30
+
31
+ it('renders the header title', () => {
32
+ renderedComponent(props);
33
+ expect(screen.getByText('Drawer Title')).toBeInTheDocument();
34
+ });
35
+
36
+ it('renders the footer content', () => {
37
+ renderedComponent(props);
38
+ expect(screen.getByText('Footer Content')).toBeInTheDocument();
39
+ });
40
+
41
+ it('renders the children content', () => {
42
+ renderedComponent(props);
43
+ expect(screen.getByText('Drawer Content')).toBeInTheDocument();
44
+ });
45
+
46
+ it('passes through the className prop', () => {
47
+ renderedComponent({ ...props, className: 'drawer-class' });
48
+ expect(screen.getByRole('dialog')).toHaveClass('drawer-class');
49
+ });
50
+
51
+ it('calls onClose when the close button is clicked', async () => {
52
+ renderedComponent(props);
53
+ await userEvent.click(screen.getByLabelText('Close'));
54
+ expect(props.onClose).toHaveBeenCalled();
55
+ });
56
+
57
+ it('calls onClose if the dimmer is clicked', async () => {
58
+ render(<Drawer {...props} />);
59
+ await userEvent.click(screen.getByRole('button'));
60
+ expect(props.onClose).toHaveBeenCalledTimes(1);
61
+ });
62
+
63
+ it('does not call onClose if the drawer content is clicked', async () => {
64
+ render(<Drawer {...props}>Drawer Content</Drawer>);
65
+ await userEvent.click(screen.getByText('Drawer Content'));
66
+ expect(props.onClose).not.toHaveBeenCalled();
67
+ });
68
+
69
+ it('does not render the Drawer when open is false', () => {
70
+ renderedComponent({ ...props, open: false });
71
+ expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
72
+ });
73
+
74
+ it('renders the Drawer with the correct aria-labelledby attribute', () => {
75
+ renderedComponent(props);
76
+ expect(screen.getByRole('dialog')).toHaveAttribute('aria-labelledby');
77
+ });
78
+
79
+ it('renders the Drawer with the correct position', () => {
80
+ renderedComponent({ ...props, position: Position.LEFT });
81
+ expect(screen.getByRole('dialog').parentElement).toHaveClass('sliding-panel--open-left');
82
+ });
83
+
84
+ it('renders the Drawer with the correct role attribute', () => {
85
+ renderedComponent(props);
86
+ expect(screen.getByRole('dialog')).toHaveAttribute('role', 'dialog');
87
+ });
88
+
89
+ it('renders the Drawer with the correct aria-modal attribute', () => {
90
+ renderedComponent(props);
91
+ expect(screen.getByRole('dialog')).toHaveAttribute('aria-modal', 'true');
92
+ });
93
+ });
package/src/index.ts CHANGED
@@ -96,6 +96,17 @@ export type { UploadProps } from './upload';
96
96
  export type { UploadError, UploadResponse, UploadedFile } from './uploadInput/types';
97
97
  export type { WithIdProps } from './withId';
98
98
  export type { IconButtonProps } from './iconButton';
99
+ export type {
100
+ TableProps,
101
+ TableRowType,
102
+ TableRowClickableType,
103
+ TableHeaderType,
104
+ TableCellLeading,
105
+ TableCellText,
106
+ TableCellCurrency,
107
+ TableCellStatus,
108
+ TableCellType,
109
+ } from './table';
99
110
 
100
111
  /**
101
112
  * Components
@@ -196,6 +207,7 @@ export { default as Tooltip } from './tooltip';
196
207
  export { default as Typeahead } from './typeahead';
197
208
  export { default as Upload } from './upload';
198
209
  export { default as UploadInput } from './uploadInput';
210
+ export { default as Table } from './table';
199
211
 
200
212
  /**
201
213
  * Hooks
package/src/main.css CHANGED
@@ -531,7 +531,8 @@ div.critical-comms .critical-comms-body {
531
531
  margin-right: calc(var(--np-avatar-layout-size) - var(--np-avatar-size) * 2);
532
532
  }
533
533
  .np-avatar-layout-horizontal {
534
- width: calc(var(--np-avatar-size) * 2 - var(--np-avatar-offset));
534
+ width: calc(var(--np-avatar-size) * var(--np-avatar-avatars-count) - (var(--np-avatar-offset) * (var(--np-avatar-avatars-count) - 1)));
535
+ width: calc(var(--np-avatar-size) * var(--np-avatar-avatars-count) - calc(var(--np-avatar-offset) * calc(var(--np-avatar-avatars-count) - 1)));
535
536
  height: var(--np-avatar-layout-size);
536
537
  }
537
538
  .np-avatar-layout-horizontal-mask {
@@ -3914,6 +3915,17 @@ html:not([dir="rtl"]) .np-navigation-option {
3914
3915
  margin-left: 0;
3915
3916
  margin-right: 1px;
3916
3917
  }
3918
+ .wds-nudge-media-flower {
3919
+ margin-left: -24px;
3920
+ margin-top: 11px;
3921
+ position: absolute;
3922
+ width: 156px;
3923
+ }
3924
+ [dir="rtl"] .wds-nudge-media-flower {
3925
+ transform: scaleX(-1);
3926
+ margin-left: 0;
3927
+ margin-right: -24px;
3928
+ }
3917
3929
  .wds-nudge-container {
3918
3930
  align-items: stretch;
3919
3931
  display: flex;
@@ -136,6 +136,17 @@
136
136
  margin-left: 0;
137
137
  margin-right: 1px;
138
138
  }
139
+ .wds-nudge-media-flower {
140
+ margin-left: -24px;
141
+ margin-top: 11px;
142
+ position: absolute;
143
+ width: 156px;
144
+ }
145
+ [dir="rtl"] .wds-nudge-media-flower {
146
+ transform: scaleX(-1);
147
+ margin-left: 0;
148
+ margin-right: -24px;
149
+ }
139
150
  .wds-nudge-container {
140
151
  align-items: stretch;
141
152
  display: flex;
@@ -65,6 +65,9 @@
65
65
  &-shopping-bag {
66
66
  .media-position(116px, 1px, 14px);
67
67
  }
68
+ &-flower {
69
+ .media-position(156px, -24px, 11px);
70
+ }
68
71
  }
69
72
 
70
73
  &-container {
@@ -104,6 +104,16 @@ export const Default = () => {
104
104
  onClick={action('action clicked')}
105
105
  onDismiss={action('dismissed')}
106
106
  />
107
+
108
+ <Nudge
109
+ mediaName={Assets.FLOWER}
110
+ className="m-b-2"
111
+ title="Text that is no longer than two lines."
112
+ link="Link"
113
+ href="#"
114
+ onClick={action('action clicked')}
115
+ onDismiss={action('dismissed')}
116
+ />
107
117
  </div>
108
118
  );
109
119
  };
@@ -38,7 +38,8 @@ type MediaNameType =
38
38
  | `${Assets.BUSINESS_CARD}`
39
39
  | `${Assets.HEART}`
40
40
  | `${Assets.MULTI_CURRENCY}`
41
- | `${Assets.SHOPPING_BAG}`;
41
+ | `${Assets.SHOPPING_BAG}`
42
+ | `${Assets.FLOWER}`;
42
43
 
43
44
  type BaseProps = {
44
45
  /** @deprecated use `mediaName` property instead */
@@ -4,7 +4,7 @@ import { Meta, StoryObj } from '@storybook/react';
4
4
  import { Status } from '../common';
5
5
  import UploadInput, { UploadInputProps } from './UploadInput';
6
6
  import { UploadedFile, UploadResponse } from './types';
7
- import { userEvent, within } from '@storybook/test';
7
+ import { userEvent } from '@storybook/test';
8
8
 
9
9
  const meta: Meta<typeof UploadInput> = {
10
10
  title: 'Forms/UploadInput/Tests',
@@ -65,8 +65,8 @@ const createDelayedPromise = async ({
65
65
  });
66
66
 
67
67
  const props = {
68
- onUploadFile: async (formData: FormData) => createDelayedPromise(),
69
- onDeleteFile: async (id: string | number) => createDelayedPromise(),
68
+ onUploadFile: async () => createDelayedPromise(),
69
+ onDeleteFile: async () => createDelayedPromise(),
70
70
  };
71
71
 
72
72
  export const UploadInputWithDescriptionFromProps: Story = {
@@ -268,7 +268,7 @@ export const DeletingTop: Story = {
268
268
  files: [files[0], files[1], files[2]],
269
269
  multiple: true,
270
270
  },
271
- play: async ({ canvasElement }) => {
271
+ play: async () => {
272
272
  await userEvent.tab();
273
273
  await triggerModalAndConfirm();
274
274
  await triggerModalAndConfirm({ isLink: false });
@@ -282,7 +282,7 @@ export const DeletingBottom: Story = {
282
282
  files: [files[0], files[1], files[2]],
283
283
  multiple: true,
284
284
  },
285
- play: async ({ canvasElement }) => {
285
+ play: async () => {
286
286
  await userEvent.tab();
287
287
  await userEvent.tab();
288
288
  await userEvent.tab();
@@ -38,4 +38,16 @@ describe('getAllowedFileTypes', () => {
38
38
  expect(allowedFileTypes).toStrictEqual(['*']);
39
39
  });
40
40
  });
41
+
42
+ describe('using only mime types', () => {
43
+ const mimeTypes = ['application/json', 'image/jpeg'];
44
+
45
+ beforeAll(() => {
46
+ allowedFileTypes = getAllowedFileTypes(mimeTypes);
47
+ });
48
+
49
+ it('returns the original mime types', () => {
50
+ expect(allowedFileTypes).toStrictEqual(['JSON', 'JPG, JPEG']);
51
+ });
52
+ });
41
53
  });
@@ -1,12 +1,38 @@
1
1
  import { FileType } from '../../common';
2
2
 
3
+ const parseFileType = (fileType: string): string => {
4
+ if (fileType?.includes('.')) {
5
+ return fileType.replace('.', '').toUpperCase();
6
+ }
7
+
8
+ const mimeType = fileType?.split('/');
9
+ if (mimeType?.length > 1) {
10
+ let parsedType = mimeType[1];
11
+
12
+ if (parsedType.toLocaleLowerCase() === 'jpeg') {
13
+ parsedType = 'jpg, '.concat(parsedType).toUpperCase();
14
+ }
15
+
16
+ return parsedType.toUpperCase();
17
+ }
18
+
19
+ return fileType;
20
+ };
21
+
3
22
  const getAllowedFileTypes = (fileTypes: readonly FileType[] | readonly string[]): string[] =>
4
- fileTypes.map((fileTypeDefinition: string) =>
5
- fileTypeDefinition
6
- .split(',')
7
- .filter((extension) => !extension.includes('/')) // Filter out mime types
8
- .map((extension) => extension.replace('.', '').toUpperCase()) // Remove dot and convert extensions to uppercase to be displayed in the instructions
9
- .join(', '),
10
- );
23
+ fileTypes.map((fileType: string) => {
24
+ const splittedFileTypes = fileType?.split(',');
25
+
26
+ if (splittedFileTypes?.length > 1) {
27
+ // If `fileType` contains `format` and `mime` types, remove mime types, proceed only with format types
28
+ return splittedFileTypes
29
+ .filter((splittedFileType: string) => !splittedFileType?.includes('/'))
30
+ .map((splittedFileType: string) => parseFileType(splittedFileType))
31
+ .join(', ');
32
+ }
33
+
34
+ // If `fileType` contains only `format` or `mime` type, parse the type
35
+ return parseFileType(fileType);
36
+ });
11
37
 
12
38
  export default getAllowedFileTypes;
@@ -1,59 +0,0 @@
1
- import { DimmerProps } from '../dimmer';
2
- import { SlidingPanelProps } from '../slidingPanel';
3
- import { mockMatchMedia, render, screen, userEvent } from '../test-utils';
4
-
5
- import Drawer from './Drawer';
6
-
7
- mockMatchMedia();
8
-
9
- jest.mock(
10
- '../dimmer',
11
- () =>
12
- function Dimmer({ open, children }: DimmerProps) {
13
- return open ? <div className="dimmer">{children}</div> : null;
14
- },
15
- );
16
-
17
- jest.mock(
18
- '../slidingPanel',
19
- () =>
20
- function SlidingPanel({ open, children }: SlidingPanelProps) {
21
- return open ? <div className="sliding-panel">{children}</div> : null;
22
- },
23
- );
24
-
25
- describe('Drawer', () => {
26
- const props = {
27
- onClose: jest.fn(),
28
- open: true,
29
- };
30
-
31
- afterEach(() => {
32
- jest.clearAllMocks();
33
- });
34
-
35
- it('renders content when open', () => {
36
- const { container } = render(<Drawer {...props}>content</Drawer>);
37
-
38
- expect(container).toMatchSnapshot();
39
- });
40
-
41
- it("doesn't render content when closed", () => {
42
- const { container } = render(
43
- <Drawer {...props} open={false}>
44
- content
45
- </Drawer>,
46
- );
47
-
48
- expect(container).toMatchSnapshot();
49
- });
50
-
51
- it('calls onClose when user clicks close button', async () => {
52
- render(<Drawer {...props}>content</Drawer>);
53
- expect(props.onClose).not.toHaveBeenCalled();
54
- await userEvent.click(getCloseButton());
55
- expect(props.onClose).toHaveBeenCalledTimes(1);
56
- });
57
-
58
- const getCloseButton = () => screen.getByLabelText('Close');
59
- });
@@ -1,101 +0,0 @@
1
- import { mount } from 'enzyme';
2
-
3
- import { mockMatchMedia } from '../test-utils';
4
- import Title from '../title/Title';
5
-
6
- import Drawer from '.';
7
-
8
- mockMatchMedia();
9
- jest.mock('../common');
10
- jest.useFakeTimers();
11
-
12
- jest.mock(
13
- '../dimmer',
14
- () =>
15
- function Dimmer({ open, children }) {
16
- return open ? <div className="dimmer">{children}</div> : null;
17
- },
18
- );
19
-
20
- jest.mock(
21
- '../slidingPanel',
22
- () =>
23
- function SlidingPanel({ open, children }) {
24
- return open ? <div className="sliding-panel">{children}</div> : null;
25
- },
26
- );
27
-
28
- const defaultLocale = 'en-GB';
29
-
30
- jest.mock('react-intl', () => ({
31
- injectIntl: (Component) =>
32
- function InjectedComponent(props) {
33
- return <Component {...props} intl={{ locale: defaultLocale }} />;
34
- },
35
- useIntl: () => ({ locale: defaultLocale, formatMessage: (id) => String(id) }),
36
- defineMessages: (translations) => translations,
37
- }));
38
-
39
- describe('Drawer', () => {
40
- let component;
41
- const props = {
42
- open: true,
43
- position: 'left',
44
- onClose: jest.fn(),
45
- children: null,
46
- headerTitle: null,
47
- footerContent: null,
48
- };
49
-
50
- beforeEach(() => {
51
- component = mount(<Drawer {...props} />);
52
- });
53
-
54
- afterEach(() => {
55
- jest.clearAllMocks();
56
- component.unmount();
57
- });
58
-
59
- it('renders drawer header if title is provided', () => {
60
- expect(component.find('.np-drawer-header--title')).toHaveLength(0);
61
- component.setProps({ headerTitle: 'A title' });
62
- expect(component.find(Title)).toHaveLength(1);
63
- expect(component.find(Title).props().children).toBe('A title');
64
- });
65
-
66
- it('renders header with or without borders', () => {
67
- expect(component.find('.np-drawer-header--withborder')).toHaveLength(0);
68
- component.setProps({ headerTitle: 'A title' });
69
- expect(component.find('.np-drawer-header--withborder')).toHaveLength(1);
70
- });
71
-
72
- it('renders content if children are provided', () => {
73
- expect(component.find('.np-drawer-content')).toHaveLength(0);
74
- component.setProps({ children: 'SomeChildren' });
75
- expect(component.find('.np-drawer-content')).toHaveLength(1);
76
- });
77
-
78
- it('renders drawer footer if footerContent is provided', () => {
79
- expect(component.find('.np-drawer-footer')).toHaveLength(0);
80
- component.setProps({ footerContent: 'SomeContent' });
81
- expect(component.find('.np-drawer-footer')).toHaveLength(1);
82
- });
83
-
84
- it('passes onUnmount to Dimmer onExited prop', () => {
85
- const onUnmount = jest.fn();
86
- component.setProps({ onUnmount });
87
- component.setProps({ open: true });
88
- expect(component.find('Dimmer').prop('onExited')).toBe(onUnmount);
89
- });
90
-
91
- it('calls onUnmount when the component unmounts', () => {
92
- const onUnmount = jest.fn();
93
- component.setProps({ onUnmount });
94
- component.setProps({ open: true });
95
- expect(onUnmount).not.toHaveBeenCalled();
96
- jest.runAllTimers();
97
- component.setProps({ open: false });
98
- component.find('Dimmer').prop('onExited')();
99
- expect(onUnmount).toHaveBeenCalledTimes(1);
100
- });
101
- });
@@ -1,55 +0,0 @@
1
- // Jest Snapshot v1, https://goo.gl/fbAQLP
2
-
3
- exports[`Drawer doesn't render content when closed 1`] = `<div />`;
4
-
5
- exports[`Drawer renders content when open 1`] = `
6
- <div>
7
- <div
8
- class="dimmer"
9
- >
10
- <div
11
- class="sliding-panel"
12
- >
13
- <div
14
- aria-modal="true"
15
- class="np-drawer"
16
- role="dialog"
17
- >
18
- <div
19
- class="np-drawer-header"
20
- >
21
- <button
22
- aria-label="Close"
23
- class="np-close-button close btn-link text-no-decoration np-close-button--large"
24
- type="button"
25
- >
26
- <span
27
- class="tw-icon tw-icon-cross "
28
- data-testid="cross-icon"
29
- >
30
- <svg
31
- aria-hidden="true"
32
- fill="currentColor"
33
- focusable="false"
34
- height="24"
35
- role="none"
36
- viewBox="0 0 24 24"
37
- width="24"
38
- >
39
- <path
40
- d="m19.629 5.915-1.2-1.2-6.257 6.257-6.258-6.257-1.2 1.2 6.258 6.257-6.258 6.257 1.2 1.2 6.258-6.257 6.257 6.257 1.2-1.2-6.258-6.257 6.258-6.257Z"
41
- />
42
- </svg>
43
- </span>
44
- </button>
45
- </div>
46
- <div
47
- class="np-drawer-content"
48
- >
49
- content
50
- </div>
51
- </div>
52
- </div>
53
- </div>
54
- </div>
55
- `;