@arbor-education/design-system.components 0.19.1 → 0.21.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 (119) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/components/combobox/Combobox.js +1 -1
  3. package/dist/components/combobox/Combobox.js.map +1 -1
  4. package/dist/components/datePicker/DatePicker.js +1 -1
  5. package/dist/components/datePicker/DatePicker.js.map +1 -1
  6. package/dist/components/dateTimePicker/DateTimePicker.js +1 -1
  7. package/dist/components/dateTimePicker/DateTimePicker.js.map +1 -1
  8. package/dist/components/dropdown/DropdownContent.js +1 -1
  9. package/dist/components/dropdown/DropdownContent.js.map +1 -1
  10. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.d.ts +1 -0
  11. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.d.ts.map +1 -1
  12. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.js +3 -2
  13. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.js.map +1 -1
  14. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.stories.d.ts +1 -0
  15. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.stories.d.ts.map +1 -1
  16. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.stories.js +27 -0
  17. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.stories.js.map +1 -1
  18. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.test.js +15 -0
  19. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.test.js.map +1 -1
  20. package/dist/components/modal/Modal.js +1 -1
  21. package/dist/components/modal/Modal.js.map +1 -1
  22. package/dist/components/slideoverManager/SlideoverManager.js +1 -1
  23. package/dist/components/slideoverManager/SlideoverManager.js.map +1 -1
  24. package/dist/components/table/Table.d.ts +1 -0
  25. package/dist/components/table/Table.d.ts.map +1 -1
  26. package/dist/components/table/Table.js +1 -1
  27. package/dist/components/table/Table.js.map +1 -1
  28. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.d.ts +7 -0
  29. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.d.ts.map +1 -1
  30. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.js +2 -2
  31. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.js.map +1 -1
  32. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.stories.d.ts +15 -0
  33. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.stories.d.ts.map +1 -1
  34. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.stories.js +185 -0
  35. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.stories.js.map +1 -1
  36. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.test.d.ts.map +1 -1
  37. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.test.js +16 -0
  38. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.test.js.map +1 -1
  39. package/dist/components/table/columnFilters/BooleanFilter/BooleanFilter.js +1 -1
  40. package/dist/components/table/columnFilters/BooleanFilter/BooleanFilter.js.map +1 -1
  41. package/dist/components/table/columnFilters/TimeFilter/TimeFilter.js +1 -1
  42. package/dist/components/table/columnFilters/TimeFilter/TimeFilter.js.map +1 -1
  43. package/dist/components/tooltip/TooltipContent.js +1 -1
  44. package/dist/components/tooltip/TooltipContent.js.map +1 -1
  45. package/dist/components/userDropdown/internal/UserDropdownUserInfo.js +1 -1
  46. package/dist/components/userDropdown/internal/UserDropdownUserInfo.js.map +1 -1
  47. package/dist/index.css +9 -0
  48. package/dist/index.css.map +1 -1
  49. package/dist/index.d.ts +2 -1
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +2 -1
  52. package/dist/index.js.map +1 -1
  53. package/dist/utils/isClient.d.ts +2 -0
  54. package/dist/utils/isClient.d.ts.map +1 -0
  55. package/dist/utils/isClient.js +2 -0
  56. package/dist/utils/isClient.js.map +1 -0
  57. package/dist/utils/isClient.ssr.test.d.ts +2 -0
  58. package/dist/utils/isClient.ssr.test.d.ts.map +1 -0
  59. package/dist/utils/isClient.ssr.test.js +9 -0
  60. package/dist/utils/isClient.ssr.test.js.map +1 -0
  61. package/dist/utils/isClient.test.d.ts +2 -0
  62. package/dist/utils/isClient.test.d.ts.map +1 -0
  63. package/dist/utils/isClient.test.js +8 -0
  64. package/dist/utils/isClient.test.js.map +1 -0
  65. package/dist/utils/popupParent/PopupParentContext.d.ts.map +1 -0
  66. package/dist/utils/popupParent/PopupParentContext.js.map +1 -0
  67. package/dist/utils/popupParent/PopupParentContext.stories.d.ts.map +1 -0
  68. package/dist/utils/{PopupParentContext.stories.js → popupParent/PopupParentContext.stories.js} +4 -4
  69. package/dist/utils/popupParent/PopupParentContext.stories.js.map +1 -0
  70. package/dist/utils/popupParent/getDefaultPopupParent.d.ts +2 -0
  71. package/dist/utils/popupParent/getDefaultPopupParent.d.ts.map +1 -0
  72. package/dist/utils/{getDefaultPopupParent.js → popupParent/getDefaultPopupParent.js} +4 -0
  73. package/dist/utils/popupParent/getDefaultPopupParent.js.map +1 -0
  74. package/dist/utils/popupParent/getDefaultPopupParent.ssr.test.d.ts +2 -0
  75. package/dist/utils/popupParent/getDefaultPopupParent.ssr.test.d.ts.map +1 -0
  76. package/dist/utils/popupParent/getDefaultPopupParent.ssr.test.js +12 -0
  77. package/dist/utils/popupParent/getDefaultPopupParent.ssr.test.js.map +1 -0
  78. package/dist/utils/popupParent/getDefaultPopupParent.test.d.ts +2 -0
  79. package/dist/utils/popupParent/getDefaultPopupParent.test.d.ts.map +1 -0
  80. package/dist/utils/popupParent/getDefaultPopupParent.test.js +20 -0
  81. package/dist/utils/popupParent/getDefaultPopupParent.test.js.map +1 -0
  82. package/package.json +1 -1
  83. package/src/components/combobox/Combobox.tsx +1 -1
  84. package/src/components/datePicker/DatePicker.tsx +1 -1
  85. package/src/components/dateTimePicker/DateTimePicker.tsx +1 -1
  86. package/src/components/dropdown/DropdownContent.tsx +1 -1
  87. package/src/components/formField/inputs/selectDropdown/SelectDropdown.stories.tsx +35 -0
  88. package/src/components/formField/inputs/selectDropdown/SelectDropdown.test.tsx +41 -0
  89. package/src/components/formField/inputs/selectDropdown/SelectDropdown.tsx +4 -0
  90. package/src/components/formField/inputs/selectDropdown/selectDropdown.scss +10 -0
  91. package/src/components/modal/Modal.tsx +1 -1
  92. package/src/components/slideoverManager/SlideoverManager.tsx +1 -1
  93. package/src/components/table/Table.tsx +1 -1
  94. package/src/components/table/cellRenderers/SelectDropdownCellRenderer.stories.tsx +212 -0
  95. package/src/components/table/cellRenderers/SelectDropdownCellRenderer.test.tsx +25 -0
  96. package/src/components/table/cellRenderers/SelectDropdownCellRenderer.tsx +9 -0
  97. package/src/components/table/columnFilters/BooleanFilter/BooleanFilter.tsx +1 -1
  98. package/src/components/table/columnFilters/TimeFilter/TimeFilter.tsx +1 -1
  99. package/src/components/tooltip/TooltipContent.tsx +1 -1
  100. package/src/components/userDropdown/internal/UserDropdownUserInfo.tsx +1 -1
  101. package/src/index.ts +2 -1
  102. package/src/utils/isClient.ssr.test.ts +9 -0
  103. package/src/utils/isClient.test.ts +8 -0
  104. package/src/utils/isClient.ts +1 -0
  105. package/src/utils/{PopupParentContext.stories.tsx → popupParent/PopupParentContext.stories.tsx} +2 -2
  106. package/src/utils/popupParent/getDefaultPopupParent.ssr.test.ts +13 -0
  107. package/src/utils/popupParent/getDefaultPopupParent.test.ts +22 -0
  108. package/src/utils/{getDefaultPopupParent.ts → popupParent/getDefaultPopupParent.ts} +7 -1
  109. package/dist/utils/PopupParentContext.d.ts.map +0 -1
  110. package/dist/utils/PopupParentContext.js.map +0 -1
  111. package/dist/utils/PopupParentContext.stories.d.ts.map +0 -1
  112. package/dist/utils/PopupParentContext.stories.js.map +0 -1
  113. package/dist/utils/getDefaultPopupParent.d.ts +0 -2
  114. package/dist/utils/getDefaultPopupParent.d.ts.map +0 -1
  115. package/dist/utils/getDefaultPopupParent.js.map +0 -1
  116. /package/dist/utils/{PopupParentContext.d.ts → popupParent/PopupParentContext.d.ts} +0 -0
  117. /package/dist/utils/{PopupParentContext.js → popupParent/PopupParentContext.js} +0 -0
  118. /package/dist/utils/{PopupParentContext.stories.d.ts → popupParent/PopupParentContext.stories.d.ts} +0 -0
  119. /package/src/utils/{PopupParentContext.ts → popupParent/PopupParentContext.ts} +0 -0
@@ -203,6 +203,14 @@ const meta = {
203
203
  defaultValue: { summary: 'false' },
204
204
  },
205
205
  },
206
+ iconClickOnly: {
207
+ control: 'boolean',
208
+ description: 'When `true`, only the chevron icon opens the dropdown — clicks elsewhere in the cell fall through to ag-grid so the cell or range can be selected for copy-paste. Defaults to `false`; opt in per column via `cellRendererParams`.',
209
+ table: {
210
+ type: { summary: 'boolean' },
211
+ defaultValue: { summary: 'false' },
212
+ },
213
+ },
206
214
  },
207
215
  } satisfies Meta<typeof SelectDropdownCellRenderer>;
208
216
 
@@ -506,3 +514,207 @@ const columnDefs = [
506
514
  },
507
515
  'Combines `backgroundColor` and `fillCell` to drive a status-coloured column. `cellRendererParams` is a function so each row gets a colour based on its current value, and `cellStyle: { padding: 0 }` removes AG Grid\'s default cell padding so the tinted trigger spans the cell edge-to-edge. Text colour switches automatically between dark and white for legibility.',
508
516
  );
517
+
518
+ const GRADE_OPTIONS = [
519
+ { label: 'A*', value: 'A*' },
520
+ { label: 'A', value: 'A' },
521
+ { label: 'B', value: 'B' },
522
+ { label: 'C', value: 'C' },
523
+ { label: 'D', value: 'D' },
524
+ { label: 'E', value: 'E' },
525
+ { label: 'U', value: 'U' },
526
+ ];
527
+
528
+ const MARKSHEET_DATA = [
529
+ { name: 'Alice Johnson', english: 'A', maths: 'B', science: 'A*' },
530
+ { name: 'Bob Smith', english: 'B', maths: 'C', science: 'B' },
531
+ { name: 'Charlie Brown', english: 'C', maths: 'A', science: 'B' },
532
+ { name: 'Diana Prince', english: 'A*', maths: 'A*', science: 'A' },
533
+ ];
534
+
535
+ export const CellSelectionWithIconOnlyTrigger: Story = withDescription(
536
+ {
537
+ parameters: {
538
+ controls: { disable: true },
539
+ docs: {
540
+ source: {
541
+ language: 'tsx',
542
+ code: `
543
+ import { Table } from '@arbor-education/design-system.components';
544
+
545
+ const columnDefs = [
546
+ { field: 'name', headerName: 'Student', flex: 2 },
547
+ {
548
+ field: 'english',
549
+ headerName: 'English',
550
+ cellRenderer: 'dsSelectDropdownCellRenderer',
551
+ cellRendererParams: {
552
+ options: gradeOptions,
553
+ iconClickOnly: true,
554
+ },
555
+ },
556
+ // ...other grade columns
557
+ ];
558
+
559
+ <Table
560
+ rowData={rowData}
561
+ columnDefs={columnDefs}
562
+ cellSelection={true}
563
+ domLayout="autoHeight"
564
+ />
565
+ `.trim(),
566
+ },
567
+ },
568
+ },
569
+ render: () => (
570
+ <Table
571
+ rowData={MARKSHEET_DATA}
572
+ columnDefs={[
573
+ { field: 'name', headerName: 'Student', flex: 2 },
574
+ {
575
+ field: 'english',
576
+ headerName: 'English',
577
+ flex: 1,
578
+ editable: false,
579
+ cellRenderer: 'dsSelectDropdownCellRenderer',
580
+ cellRendererParams: { options: GRADE_OPTIONS, iconClickOnly: true },
581
+ },
582
+ {
583
+ field: 'maths',
584
+ headerName: 'Maths',
585
+ flex: 1,
586
+ editable: false,
587
+ cellRenderer: 'dsSelectDropdownCellRenderer',
588
+ cellRendererParams: { options: GRADE_OPTIONS, iconClickOnly: true },
589
+ },
590
+ {
591
+ field: 'science',
592
+ headerName: 'Science',
593
+ flex: 1,
594
+ editable: false,
595
+ cellRenderer: 'dsSelectDropdownCellRenderer',
596
+ cellRendererParams: { options: GRADE_OPTIONS, iconClickOnly: true },
597
+ },
598
+ ]}
599
+ defaultColDef={{ flex: 1, minWidth: 100 }}
600
+ cellSelection={true}
601
+ domLayout="autoHeight"
602
+ />
603
+ ),
604
+ },
605
+ 'Marksheet-style use case. Pass `iconClickOnly: true` via `cellRendererParams` to opt the column into the icon-only trigger. Click anywhere in a grade cell to select it (or shift/drag to select a range) — `Ctrl/Cmd+C` and `Ctrl/Cmd+V` then work as expected because clicks fall through to ag-grid. To change a grade, click the chevron icon to open the dropdown. Press Enter on a focused cell as a keyboard shortcut to open the dropdown without using the mouse.',
606
+ );
607
+
608
+ const GRADE_BACKGROUND_COLORS: Record<string, string> = {
609
+ 'A*': '#1F7A1F',
610
+ 'A': '#4CAF50',
611
+ 'B': '#9CCC65',
612
+ 'C': '#FFE68A',
613
+ 'D': '#FFB74D',
614
+ 'E': '#FF8A65',
615
+ 'U': '#B91C1C',
616
+ };
617
+
618
+ export const GradeSetCell: Story = withDescription(
619
+ {
620
+ parameters: {
621
+ controls: { disable: true },
622
+ docs: {
623
+ source: {
624
+ language: 'tsx',
625
+ code: `
626
+ import { Table } from '@arbor-education/design-system.components';
627
+
628
+ const GRADE_BACKGROUND_COLORS = {
629
+ 'A*': '#1F7A1F',
630
+ 'A': '#4CAF50',
631
+ 'B': '#9CCC65',
632
+ 'C': '#FFE68A',
633
+ 'D': '#FFB74D',
634
+ 'E': '#FF8A65',
635
+ 'U': '#B91C1C',
636
+ };
637
+
638
+ const columnDefs = [
639
+ { field: 'name', headerName: 'Student', flex: 2 },
640
+ {
641
+ field: 'english',
642
+ headerName: 'English',
643
+ cellRenderer: 'dsSelectDropdownCellRenderer',
644
+ cellRendererParams: (params) => ({
645
+ options: gradeOptions,
646
+ backgroundColor: GRADE_BACKGROUND_COLORS[params.value],
647
+ fillCell: true,
648
+ iconClickOnly: true,
649
+ }),
650
+ cellStyle: { padding: 0 },
651
+ },
652
+ // ...other grade columns
653
+ ];
654
+
655
+ <Table
656
+ rowData={rowData}
657
+ columnDefs={columnDefs}
658
+ cellSelection={true}
659
+ domLayout="autoHeight"
660
+ />
661
+ `.trim(),
662
+ },
663
+ },
664
+ },
665
+ render: () => (
666
+ <Table
667
+ rowData={MARKSHEET_DATA}
668
+ columnDefs={[
669
+ { field: 'name', headerName: 'Student', flex: 2 },
670
+ {
671
+ field: 'english',
672
+ headerName: 'English',
673
+ flex: 1,
674
+ editable: false,
675
+ cellRenderer: 'dsSelectDropdownCellRenderer',
676
+ cellRendererParams: (params: { value?: string }) => ({
677
+ options: GRADE_OPTIONS,
678
+ backgroundColor: params.value ? GRADE_BACKGROUND_COLORS[params.value] : undefined,
679
+ fillCell: true,
680
+ iconClickOnly: true,
681
+ }),
682
+ cellStyle: { padding: 0 },
683
+ },
684
+ {
685
+ field: 'maths',
686
+ headerName: 'Maths',
687
+ flex: 1,
688
+ editable: false,
689
+ cellRenderer: 'dsSelectDropdownCellRenderer',
690
+ cellRendererParams: (params: { value?: string }) => ({
691
+ options: GRADE_OPTIONS,
692
+ backgroundColor: params.value ? GRADE_BACKGROUND_COLORS[params.value] : undefined,
693
+ fillCell: true,
694
+ iconClickOnly: true,
695
+ }),
696
+ cellStyle: { padding: 0 },
697
+ },
698
+ {
699
+ field: 'science',
700
+ headerName: 'Science',
701
+ flex: 1,
702
+ editable: false,
703
+ cellRenderer: 'dsSelectDropdownCellRenderer',
704
+ cellRendererParams: (params: { value?: string }) => ({
705
+ options: GRADE_OPTIONS,
706
+ backgroundColor: params.value ? GRADE_BACKGROUND_COLORS[params.value] : undefined,
707
+ fillCell: true,
708
+ iconClickOnly: true,
709
+ }),
710
+ cellStyle: { padding: 0 },
711
+ },
712
+ ]}
713
+ defaultColDef={{ flex: 1, minWidth: 100 }}
714
+ cellSelection={true}
715
+ domLayout="autoHeight"
716
+ />
717
+ ),
718
+ },
719
+ '(fillCell + colored) Marksheet with grade-driven cell colours. Each cell uses `backgroundColor` (derived per-row from the current grade) and `fillCell: true` so the colour spans the cell edge-to-edge, plus `cellStyle: { padding: 0 }` to strip ag-grid\'s default cell padding. `iconClickOnly: true` is set so cell selection works — click anywhere in a cell to select it (or shift/drag for a range), and only the chevron icon opens the grade picker. Text colour auto-switches between dark and white for legibility against each background.',
720
+ );
@@ -1,5 +1,6 @@
1
1
  import { describe, expect, test, vi } from 'vitest';
2
2
  import { render, screen } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
3
4
  import '@testing-library/jest-dom/vitest';
4
5
  import type { CustomCellRendererProps } from 'ag-grid-react';
5
6
  import type { GridApi } from 'ag-grid-community';
@@ -19,6 +20,8 @@ const createMockProps = (
19
20
  overrides: Partial<CustomCellRendererProps> & {
20
21
  backgroundColor?: string;
21
22
  fillCell?: boolean;
23
+ placeholder?: string;
24
+ iconClickOnly?: boolean;
22
25
  } = {},
23
26
  ) => ({
24
27
  value: 'option1',
@@ -105,6 +108,28 @@ describe('SelectDropdownCellRenderer', () => {
105
108
  });
106
109
  });
107
110
 
111
+ describe('iconClickOnly', () => {
112
+ test('defaults to false — modifier is not applied', () => {
113
+ render(<SelectDropdownCellRenderer {...createMockProps()} />);
114
+ expect(screen.getByRole('button')).not.toHaveClass('ds-select-dropdown--icon-click-only');
115
+ });
116
+
117
+ test('applies the icon-click-only modifier when opted in', () => {
118
+ render(
119
+ <SelectDropdownCellRenderer
120
+ {...createMockProps({ iconClickOnly: true })}
121
+ />,
122
+ );
123
+ expect(screen.getByRole('button')).toHaveClass('ds-select-dropdown--icon-click-only');
124
+ });
125
+
126
+ test('clicking the trigger opens the dropdown', async () => {
127
+ render(<SelectDropdownCellRenderer {...createMockProps({ value: '' })} />);
128
+ await userEvent.click(screen.getByRole('button'));
129
+ expect(await screen.findByText('Option 1')).toBeInTheDocument();
130
+ });
131
+ });
132
+
108
133
  test('applies both modifiers when backgroundColor and fillCell are set together', () => {
109
134
  const { container } = render(
110
135
  <SelectDropdownCellRenderer
@@ -17,6 +17,13 @@ type SelectDropdownCellRendererProps = CustomCellRendererProps
17
17
  & {
18
18
  backgroundColor?: string;
19
19
  fillCell?: boolean;
20
+ /**
21
+ * When true, only the chevron icon opens the dropdown so the rest
22
+ * of the cell remains free for ag-grid cell/range selection and
23
+ * copy-paste. Defaults to false — set it via `cellRendererParams`
24
+ * to opt in per column.
25
+ */
26
+ iconClickOnly?: boolean;
20
27
  };
21
28
 
22
29
  type SelectDropdownCellRendererOption = Omit<
@@ -44,6 +51,7 @@ export const SelectDropdownCellRenderer = (
44
51
  alwaysShowPlaceholder = false,
45
52
  backgroundColor,
46
53
  fillCell = false,
54
+ iconClickOnly = false,
47
55
  } = props;
48
56
 
49
57
  const textContrast = backgroundColor ? getTextContrast(backgroundColor) : null;
@@ -104,6 +112,7 @@ export const SelectDropdownCellRenderer = (
104
112
  open={isOpen}
105
113
  onOpenChange={setIsOpen}
106
114
  multiple={false}
115
+ iconClickOnly={iconClickOnly}
107
116
  onSelectionChange={(newValue) => {
108
117
  if (column && newValue[0] != null) {
109
118
  const selectedOption = rawOptions.find(
@@ -2,7 +2,7 @@ import type { DoesFilterPassParams } from 'ag-grid-community';
2
2
  import type { ChangeEvent } from 'react';
3
3
  import { useEffect, useRef, useState } from 'react';
4
4
  import { RadioButtonGroup } from 'Components/formField/inputs/radio/RadioButtonGroup';
5
- import { PopupParentContext } from 'Utils/PopupParentContext';
5
+ import { PopupParentContext } from 'Utils/popupParent/PopupParentContext';
6
6
 
7
7
  type BooleanFilterModel = { allowTrue: boolean; allowFalse: boolean };
8
8
 
@@ -4,7 +4,7 @@ import { compareTimeByFilterType, type FilterType, FILTER_TYPES } from './utils/
4
4
  import { transformTimeStringToDate } from './utils/transformTimeStringToDate.js';
5
5
  import { FilterResetButton } from '../filterResetButton/FilterResetButton.js';
6
6
  import { SelectDropdown } from 'Components/formField/inputs/selectDropdown/SelectDropdown';
7
- import { PopupParentContext } from 'Utils/PopupParentContext';
7
+ import { PopupParentContext } from 'Utils/popupParent/PopupParentContext';
8
8
 
9
9
  type TimeFilterModel = {
10
10
  filterType: FilterType;
@@ -1,7 +1,7 @@
1
1
  import classNames from 'classnames';
2
2
  import { Tooltip as TooltipPrimitive } from 'radix-ui';
3
3
  import { useContext } from 'react';
4
- import { PopupParentContext } from 'Utils/PopupParentContext';
4
+ import { PopupParentContext } from 'Utils/popupParent/PopupParentContext';
5
5
 
6
6
  export type TooltipContentProps = {
7
7
  children: React.ReactNode;
@@ -3,7 +3,7 @@ import { DropdownMenu } from 'radix-ui';
3
3
  import { Avatar } from 'Components/avatar/Avatar';
4
4
  import { Icon } from 'Components/icon/Icon';
5
5
  import { Dropdown } from 'Components/dropdown/Dropdown';
6
- import { PopupParentContext } from 'Utils/PopupParentContext';
6
+ import { PopupParentContext } from 'Utils/popupParent/PopupParentContext';
7
7
  import type { UserDropdownUser, UserDropdownUserInfoAction } from '../UserDropdown.js';
8
8
 
9
9
  type UserDropdownUserInfoProps = {
package/src/index.ts CHANGED
@@ -55,8 +55,9 @@ export { TooltipWrapper } from 'Components/tooltip/TooltipWrapper';
55
55
  export { GovhubLogo, KeyLogo, RobinLogo, SampeopleLogo, TimetablerLogo } from 'Components/userDropdown/assets/logos';
56
56
  export { TreeRow, type TreeRowItem, type TreeRowProps, type TreeRowSectionProps } from 'Components/treeRow/TreeRow';
57
57
  export { UserDropdown } from 'Components/userDropdown/UserDropdown';
58
+ export { isClient } from 'Utils/isClient';
58
59
  export { ModalUtils } from 'Utils/ModalUtils';
59
- export { PopupParentContext } from 'Utils/PopupParentContext';
60
+ export { PopupParentContext } from 'Utils/popupParent/PopupParentContext';
60
61
  export { SlideoverUtils } from 'Utils/SlideoverUtils';
61
62
  export { ArborLogo, type ArborLogoProps } from 'Components/arborLogo/ArborLogo';
62
63
  export { DataViewCard, type DataViewCardProps } from 'Components/dataViewCard/DataViewCard';
@@ -0,0 +1,9 @@
1
+ // @vitest-environment node
2
+ import { describe, expect, it } from 'vitest';
3
+ import { isClient } from './isClient.js';
4
+
5
+ describe('isClient', () => {
6
+ it('returns false in a non-browser environment', () => {
7
+ expect(isClient()).toBe(false);
8
+ });
9
+ });
@@ -0,0 +1,8 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { isClient } from './isClient.js';
3
+
4
+ describe('isClient', () => {
5
+ it('returns true in a browser environment', () => {
6
+ expect(isClient()).toBe(true);
7
+ });
8
+ });
@@ -0,0 +1 @@
1
+ export const isClient = (): boolean => typeof document !== 'undefined';
@@ -7,7 +7,7 @@ import {
7
7
  Subtitle,
8
8
  Title,
9
9
  } from '@storybook/addon-docs/blocks';
10
- import { PopupParentContext } from 'Utils/PopupParentContext';
10
+ import { PopupParentContext } from 'Utils/popupParent/PopupParentContext';
11
11
  import { Button } from 'Components/button/Button';
12
12
  import { Dropdown } from 'Components/dropdown/Dropdown';
13
13
 
@@ -151,7 +151,7 @@ function PopupParentContextDocsPage() {
151
151
  // ---------------------------------------------------------------------------
152
152
 
153
153
  const meta = {
154
- title: 'Utils/PopupParentContext',
154
+ title: 'Utils/popupParent/PopupParentContext',
155
155
  parameters: {
156
156
  layout: 'padded',
157
157
  docs: {
@@ -0,0 +1,13 @@
1
+ // @vitest-environment node
2
+ import { describe, expect, it } from 'vitest';
3
+
4
+ describe('getDefaultPopupParent in a non-browser environment', () => {
5
+ it('returns null without accessing document', async () => {
6
+ const { getDefaultPopupParent } = await import('./getDefaultPopupParent');
7
+ expect(getDefaultPopupParent()).toBeNull();
8
+ });
9
+
10
+ it('PopupParentContext loads without throwing', async () => {
11
+ await expect(import('./PopupParentContext')).resolves.toBeDefined();
12
+ });
13
+ });
@@ -0,0 +1,22 @@
1
+ import { afterEach, describe, expect, it } from 'vitest';
2
+ import { getDefaultPopupParent } from './getDefaultPopupParent.js';
3
+
4
+ describe('getDefaultPopupParent in a browser environment', () => {
5
+ afterEach(() => {
6
+ document.getElementById('ds-popup-parent')?.remove();
7
+ });
8
+
9
+ it('creates and appends the ds-popup-parent element to document.body', () => {
10
+ const result = getDefaultPopupParent();
11
+ expect(result).not.toBeNull();
12
+ expect(result?.id).toBe('ds-popup-parent');
13
+ expect(document.body.contains(result)).toBe(true);
14
+ });
15
+
16
+ it('returns the existing element on subsequent calls without creating a duplicate', () => {
17
+ const first = getDefaultPopupParent();
18
+ const second = getDefaultPopupParent();
19
+ expect(first).toBe(second);
20
+ expect(document.querySelectorAll('#ds-popup-parent')).toHaveLength(1);
21
+ });
22
+ });
@@ -1,4 +1,10 @@
1
- export const getDefaultPopupParent = () => {
1
+ import { isClient } from 'Utils/isClient';
2
+
3
+ export const getDefaultPopupParent = (): HTMLElement | null => {
4
+ if (!isClient()) {
5
+ return null;
6
+ }
7
+
2
8
  const element = document.getElementById('ds-popup-parent');
3
9
  if (element) {
4
10
  return element;
@@ -1 +0,0 @@
1
- {"version":3,"file":"PopupParentContext.d.ts","sourceRoot":"","sources":["../../src/utils/PopupParentContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGtD,eAAO,MAAM,kBAAkB,wDAE7B,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"PopupParentContext.js","sourceRoot":"","sources":["../../src/utils/PopupParentContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAkB,MAAM,OAAO,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAEnE,MAAM,CAAC,MAAM,kBAAkB,GAAG,aAAa,CAAgC;IAC7E,OAAO,EAAE,qBAAqB,EAAE;CACjC,CAAC,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"PopupParentContext.stories.d.ts","sourceRoot":"","sources":["../../src/utils/PopupParentContext.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAkI5D,iBAAS,0BAA0B,4CAelC;AAMD,QAAA,MAAM,IAAI;;;;;;;;;CASM,CAAC;AAEjB,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC;AA4GtB,eAAO,MAAM,OAAO,EAAE,KAuCrB,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,KAqDjC,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"PopupParentContext.stories.js","sourceRoot":"","sources":["../../src/utils/PopupParentContext.stories.tsx"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export declare const getDefaultPopupParent: () => HTMLElement;
2
- //# sourceMappingURL=getDefaultPopupParent.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"getDefaultPopupParent.d.ts","sourceRoot":"","sources":["../../src/utils/getDefaultPopupParent.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,qBAAqB,mBAmBjC,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"getDefaultPopupParent.js","sourceRoot":"","sources":["../../src/utils/getDefaultPopupParent.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,EAAE;IACxC,MAAM,OAAO,GAAG,QAAQ,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC;IAC3D,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,YAAY,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IACnD,YAAY,CAAC,EAAE,GAAG,iBAAiB,CAAC;IACpC,YAAY,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC;IAChC,YAAY,CAAC,KAAK,CAAC,QAAQ,GAAG,OAAO,CAAC;IACtC,4EAA4E;IAC5E,2EAA2E;IAC3E,0EAA0E;IAC1E,4EAA4E;IAC5E,8DAA8D;IAC9D,YAAY,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC;IAC/B,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;IAExC,OAAO,YAA2B,CAAC;AACrC,CAAC,CAAC"}