@arbor-education/design-system.components 4.0.0 → 4.1.0

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 (43) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/components/widget/types.d.ts +9 -0
  3. package/dist/components/widget/types.d.ts.map +1 -1
  4. package/dist/components/widgetCalendar/WidgetCalendar.d.ts +4 -2
  5. package/dist/components/widgetCalendar/WidgetCalendar.d.ts.map +1 -1
  6. package/dist/components/widgetCalendar/WidgetCalendar.js +30 -5
  7. package/dist/components/widgetCalendar/WidgetCalendar.js.map +1 -1
  8. package/dist/components/widgetCalendar/WidgetCalendar.stories.d.ts +18 -2
  9. package/dist/components/widgetCalendar/WidgetCalendar.stories.d.ts.map +1 -1
  10. package/dist/components/widgetCalendar/WidgetCalendar.stories.js +231 -2
  11. package/dist/components/widgetCalendar/WidgetCalendar.stories.js.map +1 -1
  12. package/dist/components/widgetCalendar/WidgetCalendar.test.js +131 -1
  13. package/dist/components/widgetCalendar/WidgetCalendar.test.js.map +1 -1
  14. package/dist/components/widgetFavourites/WidgetFavourites.d.ts.map +1 -1
  15. package/dist/components/widgetFavourites/WidgetFavourites.js +5 -4
  16. package/dist/components/widgetFavourites/WidgetFavourites.js.map +1 -1
  17. package/dist/components/widgetFavourites/WidgetFavourites.stories.d.ts +3 -0
  18. package/dist/components/widgetFavourites/WidgetFavourites.stories.d.ts.map +1 -1
  19. package/dist/components/widgetFavourites/WidgetFavourites.stories.js +157 -0
  20. package/dist/components/widgetFavourites/WidgetFavourites.stories.js.map +1 -1
  21. package/dist/components/widgetFavourites/WidgetFavourites.test.js +41 -1
  22. package/dist/components/widgetFavourites/WidgetFavourites.test.js.map +1 -1
  23. package/dist/components/widgetFavourites/WidgetFavouritesItem.d.ts.map +1 -1
  24. package/dist/components/widgetFavourites/WidgetFavouritesItem.js +2 -2
  25. package/dist/components/widgetFavourites/WidgetFavouritesItem.js.map +1 -1
  26. package/dist/components/widgetFavourites/types.d.ts +4 -2
  27. package/dist/components/widgetFavourites/types.d.ts.map +1 -1
  28. package/dist/index.css +16 -0
  29. package/dist/index.css.map +1 -1
  30. package/package.json +1 -1
  31. package/src/components/widget/types.ts +13 -0
  32. package/src/components/widgetCalendar/WidgetCalendar.stories.tsx +268 -2
  33. package/src/components/widgetCalendar/WidgetCalendar.test.tsx +242 -0
  34. package/src/components/widgetCalendar/WidgetCalendar.tsx +76 -32
  35. package/src/components/widgetCalendar/widgetCalendar.scss +9 -0
  36. package/src/components/widgetFavourites/WidgetFavourites.stories.tsx +217 -0
  37. package/src/components/widgetFavourites/WidgetFavourites.test.tsx +99 -0
  38. package/src/components/widgetFavourites/WidgetFavourites.tsx +6 -2
  39. package/src/components/widgetFavourites/WidgetFavouritesItem.tsx +2 -1
  40. package/src/components/widgetFavourites/types.ts +5 -2
  41. package/src/components/widgetFavourites/widgetFavourites.scss +7 -0
  42. package/src/tokens.scss +1 -0
  43. package/tokens/json/Arbor.json +4 -0
@@ -63,6 +63,14 @@ describe('WidgetFavourites', () => {
63
63
  expect(container.querySelector('.ds-widget--floating')).toBeInTheDocument();
64
64
  });
65
65
 
66
+ test('applies the bounded root class alongside a consumer className', () => {
67
+ const { container } = render(
68
+ <WidgetFavourites heading="Favourites" items={mockItems} className="consumer-class" />,
69
+ );
70
+
71
+ expect(container.querySelector('.ds-widget')).toHaveClass('ds-widget-favourites', 'consumer-class');
72
+ });
73
+
66
74
  test('renders linked widget header when headingHref is provided', () => {
67
75
  render(
68
76
  <WidgetFavourites
@@ -75,6 +83,97 @@ describe('WidgetFavourites', () => {
75
83
  expect(screen.getByRole('link', { name: 'Favourites (2)' })).toHaveAttribute('href', '/favourites');
76
84
  });
77
85
 
86
+ test('forwards linkProps to the linked header', () => {
87
+ const onClick = vi.fn();
88
+
89
+ render(
90
+ <WidgetFavourites
91
+ heading="Favourites (2)"
92
+ headingHref="/favourites"
93
+ linkProps={{ onClick, target: '_blank' }}
94
+ items={mockItems}
95
+ />,
96
+ );
97
+
98
+ const link = screen.getByRole('link', { name: 'Favourites (2)' });
99
+ fireEvent.click(link);
100
+
101
+ expect(onClick).toHaveBeenCalledTimes(1);
102
+ expect(link).toHaveAttribute('target', '_blank');
103
+ });
104
+
105
+ test('renders the linked header through renderLink', () => {
106
+ render(
107
+ <WidgetFavourites
108
+ heading="Favourites (2)"
109
+ headingHref="/favourites"
110
+ renderLink={({ href, className, children }) => (
111
+ <a href={href} className={className} data-router-link="true">
112
+ {children}
113
+ </a>
114
+ )}
115
+ items={mockItems}
116
+ />,
117
+ );
118
+
119
+ expect(screen.getByRole('link', { name: 'Favourites (2)' })).toHaveAttribute('data-router-link', 'true');
120
+ });
121
+
122
+ test('rejects heading link props without headingHref at the type level', () => {
123
+ render(
124
+ <>
125
+ {/* @ts-expect-error linkProps requires headingHref */}
126
+ <WidgetFavourites heading="Favourites" items={mockItems} linkProps={{ onClick: vi.fn() }} />
127
+ {/* @ts-expect-error renderLink requires headingHref */}
128
+ <WidgetFavourites heading="Favourites" items={mockItems} renderLink={() => <a />} />
129
+ </>,
130
+ );
131
+
132
+ expect(screen.queryByRole('link', { name: 'Favourites' })).not.toBeInTheDocument();
133
+ });
134
+
135
+ test('applies item linkProps to the row link', () => {
136
+ const onClick = vi.fn();
137
+
138
+ render(
139
+ <WidgetFavourites
140
+ heading="Favourites"
141
+ items={[{
142
+ id: 'daily',
143
+ label: 'Daily Attendance',
144
+ href: '/attendance/daily',
145
+ linkProps: { onClick, target: '_blank' },
146
+ }]}
147
+ />,
148
+ );
149
+
150
+ const link = screen.getByRole('link', { name: 'Daily Attendance' });
151
+ fireEvent.click(link);
152
+
153
+ expect(onClick).toHaveBeenCalledTimes(1);
154
+ expect(link).toHaveAttribute('target', '_blank');
155
+ expect(link).toHaveAttribute('href', '/attendance/daily');
156
+ });
157
+
158
+ test('WidgetFavourites.Item applies linkProps to the row link', () => {
159
+ const onClick = vi.fn();
160
+
161
+ render(
162
+ <ul>
163
+ <WidgetFavourites.Item
164
+ id="daily"
165
+ label="Daily Attendance"
166
+ href="/attendance/daily"
167
+ linkProps={{ onClick }}
168
+ />
169
+ </ul>,
170
+ );
171
+
172
+ fireEvent.click(screen.getByRole('link', { name: 'Daily Attendance' }));
173
+
174
+ expect(onClick).toHaveBeenCalledTimes(1);
175
+ });
176
+
78
177
  test('uses default remove label on remove button', () => {
79
178
  render(
80
179
  <WidgetFavourites heading="Favourites" items={mockItems} onRemoveItem={vi.fn()} />,
@@ -1,3 +1,4 @@
1
+ import classNames from 'classnames';
1
2
  import { EmptyState } from 'Components/emptyState/EmptyState';
2
3
  import { Widget } from 'Components/widget/Widget';
3
4
  import type { Widget as WidgetComponent } from 'Components/widget/Widget';
@@ -18,6 +19,8 @@ export const WidgetFavourites = (props: WidgetFavouritesProps): React.JSX.Elemen
18
19
  tooltip,
19
20
  items,
20
21
  headingHref,
22
+ renderLink,
23
+ linkProps,
21
24
  isFloating = false,
22
25
  onRemoveItem,
23
26
  emptyState = defaultEmptyState,
@@ -31,7 +34,7 @@ export const WidgetFavourites = (props: WidgetFavouritesProps): React.JSX.Elemen
31
34
  isFloating,
32
35
  isEmpty,
33
36
  emptyState,
34
- className,
37
+ className: classNames('ds-widget-favourites', className),
35
38
  };
36
39
 
37
40
  const body = (
@@ -47,6 +50,7 @@ export const WidgetFavourites = (props: WidgetFavouritesProps): React.JSX.Elemen
47
50
  label={item.label}
48
51
  href={item.href}
49
52
  removeLabel={item.removeLabel}
53
+ linkProps={item.linkProps}
50
54
  onRemove={onRemoveItem ? () => onRemoveItem(item.id) : undefined}
51
55
  />
52
56
  ))}
@@ -60,7 +64,7 @@ export const WidgetFavourites = (props: WidgetFavouritesProps): React.JSX.Elemen
60
64
  <Widget
61
65
  {...({
62
66
  ...widgetCommonProps,
63
- ...(headingHref ? { href: headingHref } : {}),
67
+ ...(headingHref ? { href: headingHref, renderLink, linkProps } : {}),
64
68
  } as WidgetComponent.Props)}
65
69
  >
66
70
  {body}
@@ -11,6 +11,7 @@ export const WidgetFavouritesItem = (props: WidgetFavouritesItemProps): React.JS
11
11
  label,
12
12
  href,
13
13
  removeLabel = FAVOURITE_TOOLTIP.remove,
14
+ linkProps,
14
15
  onRemove,
15
16
  className,
16
17
  } = props;
@@ -20,7 +21,7 @@ export const WidgetFavouritesItem = (props: WidgetFavouritesItemProps): React.JS
20
21
  className={classNames('ds-widget-favourites__item', className)}
21
22
  data-testid={`widget-favourites-item-${id}`}
22
23
  >
23
- <a href={href} className={linkClassName}>
24
+ <a {...linkProps} href={href} className={linkClassName}>
24
25
  {label}
25
26
  </a>
26
27
  {onRemove && (
@@ -1,15 +1,17 @@
1
+ import type { WidgetHeadingLinkProps, WidgetLinkedProps } from 'Components/widget/types';
2
+
1
3
  export type WidgetFavouritesItemData = {
2
4
  id: string;
3
5
  label: string;
4
6
  href: string;
5
7
  removeLabel?: string;
8
+ linkProps?: WidgetLinkedProps['linkProps'];
6
9
  };
7
10
 
8
- export type WidgetFavouritesProps = {
11
+ export type WidgetFavouritesProps = WidgetHeadingLinkProps & {
9
12
  heading: React.ReactNode;
10
13
  tooltip?: string;
11
14
  items: WidgetFavouritesItemData[];
12
- headingHref?: string;
13
15
  isFloating?: boolean;
14
16
  onRemoveItem?: (id: string) => void;
15
17
  emptyState?: React.ReactNode;
@@ -21,6 +23,7 @@ export type WidgetFavouritesItemProps = {
21
23
  label: string;
22
24
  href: string;
23
25
  removeLabel?: string;
26
+ linkProps?: WidgetLinkedProps['linkProps'];
24
27
  onRemove?: () => void;
25
28
  className?: string;
26
29
  };
@@ -1,3 +1,10 @@
1
+ // Grid resolves its row against max-height, giving the content a definite height so only the body scrolls.
2
+ .ds-widget.ds-widget-favourites {
3
+ display: grid;
4
+ grid-template-rows: minmax(0, 1fr);
5
+ max-height: var(--widget-favourites-max-height);
6
+ }
7
+
1
8
  .ds-widget-favourites__list {
2
9
  list-style: none;
3
10
  margin: 0;
package/src/tokens.scss CHANGED
@@ -882,6 +882,7 @@
882
882
  --widget-favourites-weight-border: var(--border-weight);
883
883
  --widget-favourites-spacing-vertical: var(--spacing-small);
884
884
  --widget-favourites-spacing-horizontal: var(--spacing-small);
885
+ --widget-favourites-max-height: 375px;
885
886
  --widget-attachments-hover-color-icon: var(--color-grey-900);
886
887
  --widget-attachments-hover-color-text: var(--color-grey-900);
887
888
  --widget-attachments-hover-color-background: var(--color-grey-100);
@@ -3973,6 +3973,10 @@
3973
3973
  "spacing-horizontal": {
3974
3974
  "$type": "dimension",
3975
3975
  "$value": "{spacing.small}"
3976
+ },
3977
+ "max-height": {
3978
+ "$type": "dimension",
3979
+ "$value": "375px"
3976
3980
  }
3977
3981
  },
3978
3982
  "attachments": {