@kaizen/components 1.79.7 → 1.79.9

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaizen/components",
3
- "version": "1.79.7",
3
+ "version": "1.79.9",
4
4
  "description": "Kaizen component library",
5
5
  "author": "Geoffrey Chong <geoff.chong@cultureamp.com>",
6
6
  "homepage": "https://cultureamp.design",
@@ -3,6 +3,7 @@ import { createPortal } from 'react-dom'
3
3
  import { Transition } from '@headlessui/react'
4
4
  import classnames from 'classnames'
5
5
  import FocusLock from 'react-focus-lock'
6
+ import { useIsClientReady } from '../../utils/useIsClientReady'
6
7
  import { warn } from '../util/console'
7
8
  import { ModalContext } from './context/ModalContext'
8
9
  import styles from './GenericModal.module.scss'
@@ -38,6 +39,8 @@ export const GenericModal = ({
38
39
  const labelledByID = useId()
39
40
  const describedByID = useId()
40
41
 
42
+ const isClientReady = useIsClientReady()
43
+
41
44
  const [scrollLayer, setScrollLayer] = useState<HTMLDivElement | null>(null)
42
45
  const [modalLayer, setModalLayer] = useState<HTMLDivElement | null>(null)
43
46
 
@@ -58,6 +61,8 @@ export const GenericModal = ({
58
61
  }
59
62
 
60
63
  const focusOnAccessibleLabel = (): void => {
64
+ if (!isClientReady) return
65
+
61
66
  // Check if focus already exists within the modal
62
67
  if (modalLayer?.contains(document.activeElement)) {
63
68
  return
@@ -69,6 +74,8 @@ export const GenericModal = ({
69
74
  }
70
75
 
71
76
  const a11yWarn = (): void => {
77
+ if (!isClientReady) return
78
+
72
79
  // Ensure that consumers have provided an element that labels the modal
73
80
  // to meet ARIA accessibility guidelines.
74
81
  if (!document.getElementById(labelledByID)) {
@@ -80,6 +87,8 @@ export const GenericModal = ({
80
87
  }
81
88
 
82
89
  const preventBodyScroll = (): void => {
90
+ if (!isClientReady) return
91
+
83
92
  const hasScrollbar = window.innerWidth > document.documentElement.clientWidth
84
93
  const scrollStyles = [styles.unscrollable]
85
94
 
@@ -111,28 +120,34 @@ export const GenericModal = ({
111
120
  const onBeforeEnterHandler = (): void => {
112
121
  preventBodyScroll()
113
122
 
114
- if (onEscapeKeyup) {
123
+ if (onEscapeKeyup && isClientReady) {
115
124
  document.addEventListener('keyup', escapeKeyHandler)
116
125
  }
117
126
  }
118
127
 
119
- const cleanUpAfterClose = (): void => {
128
+ const cleanUpAfterClose = useCallback(() => {
129
+ if (!isClientReady) return
130
+
120
131
  document.documentElement.classList.remove(styles.unscrollable, styles.pseudoScrollbar)
121
132
 
122
133
  if (onEscapeKeyup) {
123
134
  document.removeEventListener('keyup', escapeKeyHandler)
124
135
  }
125
- }
136
+ }, [escapeKeyHandler, onEscapeKeyup, isClientReady])
126
137
 
127
138
  /* Ensure sure add-on styles (e.g. unscrollable) and key event is cleaned up when the modal is unmounted*/
128
- // @todo: Fix if possible - avoiding breaking in eslint upgrade
129
- // eslint-disable-next-line react-hooks/exhaustive-deps
130
- useEffect(() => () => cleanUpAfterClose(), [])
139
+ useEffect(() => () => cleanUpAfterClose(), [cleanUpAfterClose])
131
140
 
132
141
  const onAfterLeaveHandler = (): void => {
133
142
  cleanUpAfterClose()
134
143
  propsOnAfterLeave?.()
135
144
  }
145
+
146
+ // Don't render portal during SSR
147
+ if (!isClientReady) {
148
+ return <></>
149
+ }
150
+
136
151
  return createPortal(
137
152
  <Transition
138
153
  appear={true}
@@ -156,6 +156,12 @@ If a button is statically the full width of a container you can use the `isFullW
156
156
 
157
157
  For resizing on smaller screens, consider using the `className` prop to leverage CSS media or container queries, ie: `<Button className="w-full md:w-[initial]">Label</Button>`.
158
158
 
159
+ ## Use a menu to show additional actions
160
+
161
+ Instead of a split button (now a [deprecated component](https://cultureamp.atlassian.net/wiki/spaces/DES/pages/4286611457/Deprecating+Split+Buttons+-+Design+Request+for+Comment+RFC)), use a Button with text followed by a Menu to show any additional actions related to the most critical action.
162
+
163
+ <Canvas className="mb-24" of={exampleStories.ButtonMenuPattern} />
164
+
159
165
  ## Additional API options
160
166
 
161
167
  The following table is a collection of additional React Aria and native HTML props that are exposed from the React Aria API. These are not required for the implementation of `Button` but can be used to extend its functionality. Refer back to the [overview section](#overview) for the core props that enable most use cases.
@@ -391,3 +391,32 @@ export const DontExampleTertiaryButtonWithIcons: Story = {
391
391
  variant: 'tertiary',
392
392
  },
393
393
  }
394
+
395
+ export const ButtonMenuPattern: Story = {
396
+ name: 'Button + Menu Pattern',
397
+ render: () => (
398
+ <div className="flex gap-4">
399
+ <Button size="large" variant="secondary">
400
+ Edit Survey
401
+ </Button>
402
+ <MenuTrigger>
403
+ <Button
404
+ size="large"
405
+ icon={<Icon name="more_horiz" isPresentational />}
406
+ variant="secondary"
407
+ hasHiddenLabel
408
+ >
409
+ More surveys
410
+ </Button>
411
+ <MenuPopover>
412
+ <Menu>
413
+ <MenuItem>Survey 1</MenuItem>
414
+ <MenuItem>Survey 2</MenuItem>
415
+ <MenuItem>Survey 3</MenuItem>
416
+ <MenuItem>Survey 4</MenuItem>
417
+ </Menu>
418
+ </MenuPopover>
419
+ </MenuTrigger>
420
+ </div>
421
+ ),
422
+ }
@@ -83,3 +83,9 @@ Menu items can be disabled with the `isDisabled` prop.
83
83
  By default, the open/closed state of the menu is handled under the hood. In cases where you need control over the open state, use the `isOpen` and `onOpenChange` props on the `MenuTrigger` component (both props must be used for this to work).
84
84
 
85
85
  <Canvas className="mb-24" of={exampleStories.Controlled} />
86
+
87
+ ## Use a menu to show additional actions
88
+
89
+ Instead of a split button (now a [deprecated component](https://cultureamp.atlassian.net/wiki/spaces/DES/pages/4286611457/Deprecating+Split+Buttons+-+Design+Request+for+Comment+RFC)), use a Button with text followed by a Menu to show any additional actions related to the most critical action.
90
+
91
+ <Canvas className="mb-24" of={exampleStories.ButtonMenuPattern} />
@@ -128,3 +128,32 @@ export const Sections: Story = {
128
128
  </MenuTrigger>
129
129
  ),
130
130
  }
131
+
132
+ export const ButtonMenuPattern: Story = {
133
+ name: 'Button + Menu Pattern',
134
+ render: ({ defaultOpen: _, ...args }) => (
135
+ <div className="flex gap-4">
136
+ <Button size="large" variant="secondary">
137
+ Edit Survey
138
+ </Button>
139
+ <MenuTrigger {...args}>
140
+ <Button
141
+ size="large"
142
+ icon={<Icon name="more_horiz" isPresentational />}
143
+ variant="secondary"
144
+ hasHiddenLabel
145
+ >
146
+ More surveys
147
+ </Button>
148
+ <MenuPopover>
149
+ <Menu>
150
+ <MenuItem>Survey 1</MenuItem>
151
+ <MenuItem>Survey 2</MenuItem>
152
+ <MenuItem>Survey 3</MenuItem>
153
+ <MenuItem>Survey 4</MenuItem>
154
+ </Menu>
155
+ </MenuPopover>
156
+ </MenuTrigger>
157
+ </div>
158
+ ),
159
+ }