@westpac/ui 0.42.2 → 0.42.3

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 (38) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/component-type.json +1 -1
  3. package/dist/components/alert/alert.component.js +3 -1
  4. package/dist/components/alert/alert.styles.js +1 -1
  5. package/dist/components/autocomplete/autocomplete.component.d.ts +8 -1
  6. package/dist/components/autocomplete/autocomplete.component.js +6 -2
  7. package/dist/components/checkbox-group/components/checkbox-group-checkbox/checkbox-group-checkbox.component.js +5 -5
  8. package/dist/components/date-picker/date-picker.component.js +4 -1
  9. package/dist/components/date-picker/date-picker.styles.d.ts +12 -0
  10. package/dist/components/date-picker/date-picker.styles.js +4 -0
  11. package/dist/components/error-message/error-message.styles.js +1 -1
  12. package/dist/components/filter/components/filter-buttons/filter-buttons.component.js +3 -1
  13. package/dist/components/header/header.styles.js +1 -1
  14. package/dist/components/popover/components/panel/panel.styles.js +1 -1
  15. package/dist/components/popover/popover.component.js +1 -0
  16. package/dist/components/radio-group/components/radio-group-radio/radio-group-radio.component.js +5 -5
  17. package/dist/components/selector/components/selector-radio-group/components/selector-radio-group-option/selector-radio-group-option.component.js +10 -3
  18. package/dist/components/switch/switch.component.js +3 -3
  19. package/dist/css/westpac-ui.css +309 -37
  20. package/dist/css/westpac-ui.min.css +309 -37
  21. package/dist/tailwind/utils/generate-date-picker-component.d.ts +30 -7
  22. package/dist/tailwind/utils/generate-date-picker-component.js +30 -7
  23. package/package.json +2 -2
  24. package/src/components/alert/alert.component.tsx +9 -2
  25. package/src/components/alert/alert.styles.ts +1 -1
  26. package/src/components/autocomplete/autocomplete.component.tsx +29 -16
  27. package/src/components/checkbox-group/components/checkbox-group-checkbox/checkbox-group-checkbox.component.tsx +4 -4
  28. package/src/components/date-picker/date-picker.component.tsx +3 -1
  29. package/src/components/date-picker/date-picker.styles.ts +4 -0
  30. package/src/components/error-message/error-message.styles.ts +1 -1
  31. package/src/components/filter/components/filter-buttons/filter-buttons.component.tsx +4 -1
  32. package/src/components/header/header.styles.ts +1 -1
  33. package/src/components/popover/components/panel/panel.styles.ts +1 -1
  34. package/src/components/popover/popover.component.tsx +1 -0
  35. package/src/components/radio-group/components/radio-group-radio/radio-group-radio.component.tsx +4 -4
  36. package/src/components/selector/components/selector-radio-group/components/selector-radio-group-option/selector-radio-group-option.component.tsx +6 -3
  37. package/src/components/switch/switch.component.tsx +2 -2
  38. package/src/tailwind/utils/generate-date-picker-component.ts +32 -8
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import * as React from 'react';
4
- import { useMemo } from 'react';
4
+ import { ForwardedRef, forwardRef, useMemo } from 'react';
5
5
  import { mergeProps, useButton, useComboBox, useFilter, useFocusRing, useSearchField } from 'react-aria';
6
6
  import { useComboBoxState, useSearchFieldState } from 'react-stately';
7
7
 
@@ -28,25 +28,30 @@ const STATIC_IS_OPEN_STATE = {
28
28
  },
29
29
  };
30
30
 
31
- export function Autocomplete<T extends object>({
32
- size = 'medium',
33
- invalid = false,
34
- isDisabled,
35
- footer,
36
- portalContainer,
37
- errorMessage,
38
- hintMessage,
39
- noOptionsMessage,
40
- className,
41
- width = 'full',
42
- loadingState,
43
- ...props
44
- }: AutocompleteProps<T>) {
31
+ function Autocomplete<T extends object>(
32
+ {
33
+ size = 'medium',
34
+ invalid = false,
35
+ isDisabled,
36
+ footer,
37
+ portalContainer,
38
+ errorMessage,
39
+ hintMessage,
40
+ noOptionsMessage,
41
+ className,
42
+ width = 'full',
43
+ loadingState,
44
+ ...props
45
+ }: AutocompleteProps<T>,
46
+ ref: ForwardedRef<HTMLInputElement>,
47
+ ) {
45
48
  const { contains } = useFilter({ sensitivity: 'base' });
46
49
  const state = useComboBoxState({ isDisabled, ...props, defaultFilter: contains });
47
50
  const { isFocusVisible, focusProps } = useFocusRing();
48
51
  const { isFocusVisible: isInputFocusVisible, focusProps: inputFocusProps } = useFocusRing();
49
- const inputRef = React.useRef(null);
52
+ const inputRef = React.useRef<HTMLInputElement>(null);
53
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
54
+ React.useImperativeHandle(ref, () => inputRef.current!);
50
55
  const listBoxRef = React.useRef(null);
51
56
  const popoverRef = React.useRef(null);
52
57
  const { inputProps, listBoxProps, labelProps, descriptionProps, errorMessageProps } = useComboBox(
@@ -168,3 +173,11 @@ export function Autocomplete<T extends object>({
168
173
  </div>
169
174
  );
170
175
  }
176
+
177
+ const _Autocomplete = forwardRef(Autocomplete) as unknown as { displayName: string } & (<T extends object>(
178
+ props: AutocompleteProps<T> & { ref?: ForwardedRef<HTMLInputElement> },
179
+ ) => ReturnType<typeof Autocomplete>);
180
+
181
+ _Autocomplete.displayName = 'Autocomplete';
182
+
183
+ export { _Autocomplete as Autocomplete };
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import React, { Ref, forwardRef, useContext, useRef } from 'react';
4
- import { VisuallyHidden, useCheckboxGroupItem, useFocusRing } from 'react-aria';
4
+ import { VisuallyHidden, mergeProps, useCheckboxGroupItem, useFocusRing } from 'react-aria';
5
5
 
6
6
  import { Icon } from '../../../icon/icon.component.js';
7
7
  import { IconProps } from '../../../icon/index.js';
@@ -37,7 +37,7 @@ function BaseCheckbox(
37
37
  ) {
38
38
  const { state, size, orientation } = useContext(CheckboxGroupContext);
39
39
  const localRef = useRef(null);
40
- const { inputProps, isDisabled, isSelected } = useCheckboxGroupItem(
40
+ const { inputProps, labelProps, isDisabled, isSelected } = useCheckboxGroupItem(
41
41
  { ...props, value, children: label },
42
42
  state,
43
43
  localRef,
@@ -46,9 +46,9 @@ function BaseCheckbox(
46
46
  const styles = checkboxItemStyles({ isDisabled, size, orientation, isFocusVisible });
47
47
 
48
48
  return (
49
- <label className={styles.base({ className })} ref={ref}>
49
+ <label className={styles.base({ className })} ref={ref} {...labelProps}>
50
50
  <VisuallyHidden elementType="span">
51
- <input {...inputProps} {...focusProps} ref={localRef} />
51
+ <input {...mergeProps(inputProps, focusProps)} ref={localRef} />
52
52
  </VisuallyHidden>
53
53
  <span className={styles.checkbox()}>
54
54
  {isSelected && <CheckIcon className={styles.checkIcon()} size={size} color={isDisabled ? 'border' : 'hero'} />}
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
4
+ import { useFocusVisible } from 'react-aria';
4
5
 
5
6
  import { styles } from './date-picker.styles.js';
6
7
  import { type DatePickerProps, DuetDatePickerElement } from './date-picker.types.js';
@@ -28,6 +29,7 @@ export function DatePicker({
28
29
  ...props
29
30
  }: DatePickerProps) {
30
31
  const [initialized, setInitialized] = useState(false);
32
+ const { isFocusVisible } = useFocusVisible();
31
33
 
32
34
  useEffect(() => {
33
35
  const initDatePicker = async () => {
@@ -124,5 +126,5 @@ export function DatePicker({
124
126
 
125
127
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
126
128
  // @ts-ignore
127
- return <duet-date-picker class={styles({ size, block, invalid })} ref={ref} {...props} />;
129
+ return <duet-date-picker class={styles({ size, block, invalid, isFocusVisible })} ref={ref} {...props} />;
128
130
  }
@@ -18,6 +18,10 @@ export const styles = tv(
18
18
  true: 'date-picker-invalid',
19
19
  false: '',
20
20
  },
21
+ isFocusVisible: {
22
+ true: 'date-picker-focused',
23
+ false: '',
24
+ },
21
25
  },
22
26
  },
23
27
  { responsiveVariants: ['xsl', 'sm', 'md', 'lg', 'xl'] },
@@ -5,6 +5,6 @@ export const styles = tv({
5
5
  base: 'typography-body-11 flex items-start text-danger',
6
6
  list: 'mb-2 flex flex-col gap-1',
7
7
  // below should be em rather than rem based on old GEL
8
- icon: 'mr-[0.5em] mt-[0.15rem] align-top',
8
+ icon: 'mr-[0.5em] mt-[0.15rem] flex-shrink-0 align-top',
9
9
  },
10
10
  });
@@ -68,6 +68,7 @@ export function FilterButtons({
68
68
  setScroll(scrollBy, scrollX, container);
69
69
  }
70
70
  },
71
+ // eslint-disable-next-line react-hooks/exhaustive-deps
71
72
  [scrollTarget],
72
73
  );
73
74
 
@@ -116,7 +117,7 @@ export function FilterButtons({
116
117
 
117
118
  return { targetLeft, targetRight };
118
119
  },
119
- [],
120
+ [filterButtons.length],
120
121
  );
121
122
 
122
123
  const handleScrollTarget = useCallback(
@@ -141,6 +142,7 @@ export function FilterButtons({
141
142
  setScrollTarget({ left: targetLeft, right: targetRight });
142
143
  }
143
144
  },
145
+ // eslint-disable-next-line react-hooks/exhaustive-deps
144
146
  [scrollTarget, getTargetLeft, getTargetRight, adjustTargets],
145
147
  );
146
148
 
@@ -172,6 +174,7 @@ export function FilterButtons({
172
174
  container?.removeEventListener('scroll', handleScroll);
173
175
  window.removeEventListener('resize', handleScroll);
174
176
  };
177
+ // eslint-disable-next-line react-hooks/exhaustive-deps
175
178
  }, [handleScrollTarget, handleScrollable]);
176
179
 
177
180
  return (
@@ -18,7 +18,7 @@ export const styles = tv(
18
18
  logoCenter: {
19
19
  true: {
20
20
  logoLink:
21
- 'max-sm:absolute max-sm:left-1/2 max-sm:z-0 max-sm:translate-x-[-50%] max-sm:translate-y-[15%] sm:relative',
21
+ 'max-sm:absolute max-sm:left-1/2 max-sm:z-0 max-sm:-translate-x-1/2 max-sm:translate-y-[15%] sm:relative',
22
22
  smallLogo: '',
23
23
  },
24
24
  false: {
@@ -9,7 +9,7 @@ export const styles = tv(
9
9
  before:absolute before:left-[1px] before:top-[0.5px] before:size-0 before:border-x-[7px] before:border-t-[12px] before:border-x-[transparent] before:border-t-muted after:absolute
10
10
  after:left-[1.5px] after:top-0 after:size-0 after:border-x-[6.5px] after:border-t-[11px] after:border-x-[transparent] after:border-t-white
11
11
  `,
12
- closeBtn: 'absolute right-1 h-3 top-1 p-0 hover:opacity-80',
12
+ closeBtn: 'absolute right-1 top-1 h-3 p-0 hover:opacity-80',
13
13
  content: 'w-[17.625rem] rounded-[3px] bg-white py-4 pl-3 pr-5',
14
14
  heading: 'typography-body-9 mb-2 font-medium text-text',
15
15
  body: 'typography-body-10 text-text',
@@ -64,6 +64,7 @@ export function Popover({
64
64
  return (
65
65
  <div className={styles.base({ className })}>
66
66
  <Button
67
+ type="button"
67
68
  look={(icon && !children) || linkStyling ? 'link' : look}
68
69
  iconAfter={icon}
69
70
  soft={soft}
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import React, { Ref, forwardRef, useContext, useRef } from 'react';
4
- import { VisuallyHidden, useFocusRing, useRadio } from 'react-aria';
4
+ import { VisuallyHidden, mergeProps, useFocusRing, useRadio } from 'react-aria';
5
5
 
6
6
  import { RadioGroupContext } from '../../radio-group.component.js';
7
7
 
@@ -11,14 +11,14 @@ import { type RadioGroupRadioProps } from './radio-group-radio.types.js';
11
11
  function BaseRadioGroupRadio({ className, hint, label, ...props }: RadioGroupRadioProps, ref: Ref<HTMLLabelElement>) {
12
12
  const { state, size, orientation } = useContext(RadioGroupContext);
13
13
  const localRef = useRef(null);
14
- const { inputProps, isSelected, isDisabled } = useRadio({ ...props, children: label }, state, localRef);
14
+ const { inputProps, labelProps, isSelected, isDisabled } = useRadio({ ...props, children: label }, state, localRef);
15
15
  const { isFocusVisible, focusProps } = useFocusRing();
16
16
  const styles = radioStyles({ isDisabled, isSelected, isFocusVisible, size, orientation });
17
17
 
18
18
  return (
19
- <label className={styles.base({ className })} ref={ref}>
19
+ <label className={styles.base({ className })} ref={ref} {...labelProps}>
20
20
  <VisuallyHidden elementType="span">
21
- <input {...inputProps} {...focusProps} ref={localRef} />
21
+ <input {...mergeProps(inputProps, focusProps)} ref={localRef} />
22
22
  </VisuallyHidden>
23
23
  <span className={styles.selector()} />
24
24
  <span className={styles.textWrapper()}>
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import React, { Ref, forwardRef, useContext, useRef } from 'react';
3
+ import React, { Ref, forwardRef, useCallback, useContext, useRef } from 'react';
4
4
  import { VisuallyHidden, mergeProps, useFocusRing, useRadio } from 'react-aria';
5
5
 
6
6
  import { ArrowRightIcon, TickIcon } from '../../../../../../components/icon/index.js';
@@ -32,9 +32,12 @@ function BaseSelectorRadioGroupOption(
32
32
 
33
33
  const FinalIcon = checkIcon === 'checkbox' ? TickIcon : ArrowRightIcon;
34
34
 
35
- const onItemClick = () => {
35
+ const onItemClick = useCallback(() => {
36
+ if (isDisabled) {
37
+ return;
38
+ }
36
39
  state.setSelectedValue(value);
37
- };
40
+ }, [isDisabled, state, value]);
38
41
 
39
42
  return (
40
43
  <FlexiCell
@@ -20,7 +20,7 @@ export function Switch({
20
20
  const labelId = useId();
21
21
  const ref = useRef(null);
22
22
  const { isSelected } = state;
23
- const { inputProps } = useCheckbox(
23
+ const { inputProps, labelProps } = useCheckbox(
24
24
  { isDisabled, 'aria-labelledby': labelId, defaultSelected: checked, ...props },
25
25
  state,
26
26
  ref,
@@ -29,7 +29,7 @@ export function Switch({
29
29
  const styles = switchStyles({ block, isFocusVisible, isSelected, isDisabled, size });
30
30
 
31
31
  return (
32
- <label className={styles.base({ className })}>
32
+ <label className={styles.base({ className })} {...labelProps}>
33
33
  <span className={styles.label()} id={labelId}>
34
34
  {label}
35
35
  </span>
@@ -2,16 +2,15 @@ export const generateDatePicker = () => {
2
2
  return {
3
3
  '.date-picker': {
4
4
  '.duet-date__input': {
5
- '@apply text-lg focus:!border-borderDark focus:!shadow-none focus:focus-outline border border-borderDark border-solid rounded':
6
- {},
5
+ '@apply text-lg focus:!border-borderDark focus:!shadow-none border border-borderDark border-solid rounded': {},
7
6
  },
8
7
  '.duet-date__toggle': {
9
8
  '@apply shadow-none border-solid border-l !border-l-borderDark !bg-light touch-manipulation whitespace-nowrap transition hover:bg-white active:bg-white rounded-r':
10
9
  {},
11
- '@apply focus:shadow-none focus:focus-outline': {},
10
+ '@apply focus:shadow-none': {},
12
11
  },
13
12
  '.duet-date__select select:focus + .duet-date__select-label': {
14
- '@apply !focus-outline rounded': {},
13
+ '@apply rounded': {},
15
14
  },
16
15
  '.duet-date__toggle-icon': {
17
16
  '@apply w-[18px] h-[18px] bg-cover flex-auto': {},
@@ -100,17 +99,17 @@ export const generateDatePicker = () => {
100
99
  '@apply font-semibold': {},
101
100
  },
102
101
  '.duet-date__prev, .duet-date__next': {
103
- '@apply bg-background focus:focus-outline !outline-offset-0 text-primary': {},
102
+ '@apply bg-background !outline-offset-0 text-primary': {},
104
103
  },
105
104
  '.duet-date-dialog-select select:focus + .duet-date-dialog-select-label': {
106
- '@apply focus-outline !shadow-none': {},
105
+ '@apply !shadow-none': {},
107
106
  'outline-offset': '0 !important',
108
107
  },
109
108
 
110
109
  /* Calendar days */
111
110
  '.duet-date__day': {
112
111
  '@apply hover:!bg-primary/5 focus:!bg-white/0 focus:!text-text': {},
113
- '@apply active:shadow active:focus-outline focus:focus-outline focus:!shadow-none !outline-offset-0': {},
112
+ '@apply active:shadow focus:!shadow-none !outline-offset-0': {},
114
113
  '&.is-today': {
115
114
  '@apply !shadow-primary !text-text !bg-primary/5 !border !border-solid !border-primary': {},
116
115
  },
@@ -124,9 +123,34 @@ export const generateDatePicker = () => {
124
123
  },
125
124
  },
126
125
  '.duet-date__close:focus': {
127
- '@apply bg-light focus-outline': {},
126
+ '@apply bg-light': {},
128
127
  'outline-offset': '0 !important',
129
128
  },
129
+
130
+ /* Focused styles */
131
+ '&.date-picker-focused': {
132
+ '.duet-date__input': {
133
+ '@apply focus:focus-outline': {},
134
+ },
135
+ '.duet-date__toggle': {
136
+ '@apply focus:focus-outline': {},
137
+ },
138
+ '.duet-date__select select:focus + .duet-date__select-label': {
139
+ '@apply !focus-outline': {},
140
+ },
141
+ '.duet-date__prev, .duet-date__next': {
142
+ '@apply focus:focus-outline': {},
143
+ },
144
+ '.duet-date-dialog-select select:focus + .duet-date-dialog-select-label': {
145
+ '@apply focus-outline': {},
146
+ },
147
+ '.duet-date__day': {
148
+ '@apply active:focus-outline focus:focus-outline': {},
149
+ },
150
+ '.duet-date__close:focus': {
151
+ '@apply bg-light focus-outline': {},
152
+ },
153
+ },
130
154
  },
131
155
  };
132
156
  };