@codeleap/web 3.17.0 → 3.18.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeleap/web",
3
- "version": "3.17.0",
3
+ "version": "3.18.1",
4
4
  "main": "src/index.ts",
5
5
  "repository": {
6
6
  "url": "https://github.com/codeleap-uk/internal-libs-monorepo.git",
@@ -20,6 +20,7 @@ export type ActionIconProps = Omit<TouchableProps, 'styles' | 'variants'> & Comp
20
20
 
21
21
  const defaultProps: Partial<ActionIconProps> = {
22
22
  disabled: false,
23
+ tabIndex: 0,
23
24
  }
24
25
 
25
26
  export const ActionIcon = (props: ActionIconProps) => {
@@ -28,7 +29,7 @@ export const ActionIcon = (props: ActionIconProps) => {
28
29
  ...props,
29
30
  }
30
31
 
31
- const {
32
+ const {
32
33
  icon,
33
34
  name,
34
35
  iconProps,
@@ -39,30 +40,31 @@ export const ActionIcon = (props: ActionIconProps) => {
39
40
  children,
40
41
  disabled,
41
42
  debugName,
42
- ...touchableProps
43
+ ...touchableProps
43
44
  } = allProps
44
-
45
+
45
46
  const variantStyles = useDefaultComponentStyle<'u:ActionIcon', typeof ActionIconPresets>('u:ActionIcon', {
46
47
  responsiveVariants,
47
- variants,
48
+ variants,
48
49
  styles,
49
- rootElement: 'touchableWrapper'
50
+ rootElement: 'touchableWrapper',
50
51
  })
51
52
 
52
53
  const isPressable = TypeGuards.isFunction(onPress) && !disabled
53
54
 
54
55
  const WrapperComponent: any = isPressable ? Touchable : View
55
56
 
56
- const handlePress = () => {
57
+ const handlePress = (e) => {
57
58
  if (!isPressable) return
58
-
59
- if (onPress) onPress?.()
59
+ if (onPress && (e?.type === 'click' || e?.keyCode === 13 || e?.key === 'Enter')) {
60
+ onPress?.()
61
+ }
60
62
  }
61
63
 
62
64
  const getStyles = (key: ActionIconParts) => ({
63
65
  ...variantStyles[key],
64
66
  ...(disabled ? variantStyles[`${key}:disabled`] : {}),
65
- ...(isPressable ? variantStyles[`${key}:pressable`] : {})
67
+ ...(isPressable ? variantStyles[`${key}:pressable`] : {}),
66
68
  })
67
69
 
68
70
  return (
@@ -72,7 +74,8 @@ export const ActionIcon = (props: ActionIconProps) => {
72
74
  debugName={debugName}
73
75
  {
74
76
  ...(isPressable && {
75
- onPress: handlePress,
77
+ onPress: () => handlePress({ type: 'click' }),
78
+ onKeyDown: handlePress,
76
79
  })
77
80
  }
78
81
  {...touchableProps}
@@ -113,9 +113,9 @@ export const Checkbox = (props: CheckboxProps) => {
113
113
 
114
114
  const _checkboxOnLeft = checkboxOnLeft ?? variantStyles.__props?.checkboxOnLeft
115
115
 
116
- const handleChange = () => {
116
+ const handleChange = (e) => {
117
117
  if (disabled) return
118
- if (onValueChange) onValueChange?.(!value)
118
+ if (onValueChange && (e?.type === 'click' || e?.keyCode === 13 || e?.key === 'Enter')) onValueChange?.(!value)
119
119
  }
120
120
 
121
121
  return <InputBase
@@ -142,6 +142,8 @@ export const Checkbox = (props: CheckboxProps) => {
142
142
  animate={boxAnimation}
143
143
  transition={variantStyles['box:transition']}
144
144
  onClick={handleChange}
145
+ onKeyDown={handleChange}
146
+ tabIndex={0}
145
147
  >
146
148
  <motion.div
147
149
  css={[
@@ -151,6 +153,7 @@ export const Checkbox = (props: CheckboxProps) => {
151
153
  initial={false}
152
154
  animate={checkmarkWrapperAnimation}
153
155
  transition={variantStyles['checkmarkWrapper:transition']}
156
+
154
157
  >
155
158
  <Icon
156
159
  debugName={debugName}
@@ -88,7 +88,7 @@ export function Grid<T = any>(props: GridProps<T>) {
88
88
  isOnly,
89
89
  isLast,
90
90
  isFirst,
91
- item: _item?.data
91
+ item: _item?.data,
92
92
  }
93
93
 
94
94
  if (!_itemProps?.item) return null
@@ -185,7 +185,7 @@ export const ModalContent = (
185
185
  } = modalProps
186
186
 
187
187
  const id = useId()
188
-
188
+ const modalRef = useRef(null)
189
189
  const variantStyles = useDefaultComponentStyle<'u:Modal', typeof ModalPresets>('u:Modal', {
190
190
  responsiveVariants,
191
191
  variants,
@@ -210,6 +210,39 @@ export const ModalContent = (
210
210
  }
211
211
  }
212
212
 
213
+ const handleTabKeyPress = (e: React.KeyboardEvent<HTMLDivElement>, { firstElement, lastElement }: Record<'firstElement' |'lastElement', HTMLDivElement>) => {
214
+ if (e.key === 'Tab' || e?.keyCode === 9) {
215
+ if (e.shiftKey && document.activeElement === firstElement) {
216
+ e.preventDefault()
217
+ lastElement.focus()
218
+ } else if (
219
+ !e.shiftKey &&
220
+ document.activeElement === lastElement
221
+ ) {
222
+ e.preventDefault()
223
+ firstElement.focus()
224
+ }
225
+ }
226
+ }
227
+
228
+ onUpdate(() => {
229
+ if (visible) {
230
+ const modalElement = modalRef.current
231
+
232
+ const focusableElements = modalElement.querySelectorAll('[tabindex]:not([tabindex="-1"])') as NodeListOf<HTMLDivElement>
233
+ const firstElement = focusableElements[0]
234
+ const lastElement = focusableElements[focusableElements.length - 1]
235
+
236
+ modalElement.addEventListener('keydown', (e) => handleTabKeyPress(e, { firstElement, lastElement }))
237
+ modalElement.addEventListener('keydown', closeOnEscPress)
238
+
239
+ return () => {
240
+ modalElement.removeEventListener('keydown', (e) => handleTabKeyPress(e, { firstElement, lastElement }))
241
+ modalElement.removeEventListener('keydown', closeOnEscPress)
242
+ }
243
+ }
244
+ }, [visible])
245
+
213
246
  useIsomorphicEffect(() => {
214
247
  const modal = document.getElementById(id)
215
248
  if (modal) modal.focus()
@@ -227,6 +260,7 @@ export const ModalContent = (
227
260
 
228
261
  return (
229
262
  <View
263
+ ref={modalRef}
230
264
  aria-hidden={!visible}
231
265
  css={[
232
266
  variantStyles.wrapper,
@@ -266,7 +300,6 @@ export const ModalContent = (
266
300
  style,
267
301
  ]}
268
302
  className='content'
269
- onKeyDown={closeOnEscPress}
270
303
  tabIndex={0}
271
304
  id={id}
272
305
  aria-modal={true}
@@ -135,7 +135,9 @@ export const SegmentedControl = (props: SegmentedControlProps) => {
135
135
  largestWidth,
136
136
  ]
137
137
 
138
- const onSelectTab = (option: SegmentedControlOptionProps) => {
138
+ const onSelectTab = (option: SegmentedControlOptionProps, e?: React.KeyboardEvent<HTMLDivElement>) => {
139
+ if (!!e && e?.keyCode !== 13 || e?.key !== 'Enter') return null
140
+
139
141
  if (!debounceEnabled || !TypeGuards.isNumber(debounce)) {
140
142
  onValueChange(option.value)
141
143
  return
@@ -171,6 +173,7 @@ export const SegmentedControl = (props: SegmentedControlProps) => {
171
173
  label={o.label}
172
174
  value={o.value}
173
175
  onPress={() => onSelectTab(o)}
176
+ onKeyDown={(e) => onSelectTab(o, e)}
174
177
  key={idx}
175
178
  icon={o.icon}
176
179
  selected={value === o.value}
@@ -179,6 +182,7 @@ export const SegmentedControl = (props: SegmentedControlProps) => {
179
182
  disabled={disabled}
180
183
  textProps={textProps}
181
184
  iconProps={iconProps}
185
+ tabIndex={0}
182
186
  {...props?.touchableProps}
183
187
  />
184
188
  ))}
@@ -89,10 +89,12 @@ export const Switch = (props: SwitchProps) => {
89
89
 
90
90
  const _switchOnLeft = switchOnLeft ?? variantStyles.__props?.switchOnLeft
91
91
 
92
- const handleChange = () => {
92
+ const handleChange = (e?: React.KeyboardEvent<HTMLDivElement>) => {
93
93
  if (disabled) return
94
- if (onValueChange) onValueChange?.(!value)
95
- if (onChange) onChange?.(!value)
94
+ if (e?.type === 'click' || e?.keyCode === 13 || e?.key === 'Enter') {
95
+ if (onValueChange) onValueChange?.(!value)
96
+ if (onChange) onChange?.(!value)
97
+ }
96
98
  }
97
99
 
98
100
  return <InputBase
@@ -118,6 +120,8 @@ export const Switch = (props: SwitchProps) => {
118
120
  animate={trackAnimation}
119
121
  transition={variantStyles['track:transition']}
120
122
  onClick={handleChange}
123
+ onKeyDown={handleChange}
124
+ tabIndex={0}
121
125
  >
122
126
  <motion.div
123
127
  css={[
@@ -38,6 +38,7 @@ const defaultProps: TouchableProps<'button'> = {
38
38
  analyticsEnabled: false,
39
39
  analyticsName: null,
40
40
  analyticsData: {},
41
+ tabIndex: 0,
41
42
  }
42
43
  export const TouchableCP = <T extends NativeHTMLElement = 'button'>(
43
44
  touchableProps: TouchableProps<T>,
@@ -105,6 +106,8 @@ export const TouchableCP = <T extends NativeHTMLElement = 'button'>(
105
106
  }
106
107
 
107
108
  const _onPress = () => {
109
+ if (event && (event?.type !== 'click' && event?.keyCode !== 13 && event?.key !== 'Enter')) return null
110
+
108
111
  logger.log(
109
112
  `<${debugComponent || 'Touchable'}/> pressed`,
110
113
  { debugName, debugComponent },
@@ -150,6 +153,7 @@ export const TouchableCP = <T extends NativeHTMLElement = 'button'>(
150
153
  {...props}
151
154
  debugName={debugName}
152
155
  onClick={handleClick}
156
+ onKeyDown={handleClick}
153
157
  ref={ref}
154
158
  css={_styles}
155
159
  />