@westpac/ui 1.4.0 → 1.5.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.
- package/CHANGELOG.md +7 -0
- package/assets/icons/filled/flag-filled.svg +3 -0
- package/assets/icons/outlined/flag-outlined.svg +3 -0
- package/dist/component-type.json +1 -1
- package/dist/components/autocomplete/autocomplete.component.js +6 -3
- package/dist/components/error-message/error-message.component.d.ts +1 -1
- package/dist/components/error-message/error-message.component.js +26 -3
- package/dist/components/error-message/error-message.styles.d.ts +18 -0
- package/dist/components/error-message/error-message.styles.js +4 -1
- package/dist/components/error-message/error-message.types.d.ts +4 -0
- package/dist/components/field/field.component.d.ts +1 -1
- package/dist/components/field/field.component.js +2 -1
- package/dist/components/field/field.types.d.ts +4 -0
- package/dist/components/icon/components/flag-icon.d.ts +2 -0
- package/dist/components/icon/components/flag-icon.js +15 -0
- package/dist/components/icon/index.d.ts +1 -0
- package/dist/components/icon/index.js +1 -0
- package/dist/components/selector/components/selector-button-group/selector-button-group.component.d.ts +1 -1
- package/dist/components/selector/components/selector-button-group/selector-button-group.component.js +13 -4
- package/dist/components/selector/components/selector-button-group/selector-button-group.types.d.ts +6 -1
- package/package.json +2 -2
- package/src/components/autocomplete/autocomplete.component.tsx +4 -11
- package/src/components/error-message/error-message.component.tsx +27 -3
- package/src/components/error-message/error-message.styles.ts +3 -0
- package/src/components/error-message/error-message.types.ts +4 -0
- package/src/components/field/field.component.tsx +2 -1
- package/src/components/field/field.types.ts +4 -0
- package/src/components/icon/components/flag-icon.tsx +21 -0
- package/src/components/icon/index.ts +1 -0
- package/src/components/selector/components/selector-button-group/selector-button-group.component.tsx +11 -4
- package/src/components/selector/components/selector-button-group/selector-button-group.types.ts +6 -1
|
@@ -71,14 +71,17 @@ function Autocomplete({ size = 'medium', invalid = false, isDisabled, footer, po
|
|
|
71
71
|
const { buttonProps } = useButton(clearButtonProps, clearButtonRef);
|
|
72
72
|
const outerRef = React.useRef(null);
|
|
73
73
|
const isNoOptionPopOverOpen = useMemo(()=>{
|
|
74
|
-
|
|
74
|
+
var _ref;
|
|
75
|
+
var _searchProps_value;
|
|
76
|
+
const inputLength = (_ref = (_searchProps_value = searchProps.value) === null || _searchProps_value === void 0 ? void 0 : _searchProps_value.length) !== null && _ref !== void 0 ? _ref : 0;
|
|
77
|
+
return !!(noOptionsMessage && (!state.isOpen && state.isFocused && inputLength > 0 && !state.value || state.collection.size === 0 && inputLength > 0));
|
|
75
78
|
}, [
|
|
76
79
|
noOptionsMessage,
|
|
77
80
|
state.isOpen,
|
|
78
81
|
state.isFocused,
|
|
79
|
-
state.
|
|
82
|
+
state.value,
|
|
80
83
|
state.collection.size,
|
|
81
|
-
searchProps.value
|
|
84
|
+
searchProps.value
|
|
82
85
|
]);
|
|
83
86
|
return React.createElement("div", {
|
|
84
87
|
className: styles.base({
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { type ErrorMessageProps } from './error-message.types.js';
|
|
2
|
-
export declare function ErrorMessage({ className, tag: Tag, icon: Icon, message, ...props }: ErrorMessageProps): import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare function ErrorMessage({ className, tag: Tag, icon: Icon, errorTitle, message, ...props }: ErrorMessageProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,10 +1,33 @@
|
|
|
1
1
|
import { clsx } from 'clsx';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { AlertIcon } from '../../components/icon/index.js';
|
|
4
|
+
import { List, ListItem } from '../../components/list/index.js';
|
|
4
5
|
import { styles as errorMessageStyles } from './error-message.styles.js';
|
|
5
|
-
export function ErrorMessage({ className, tag: Tag = 'div', icon: Icon, message, ...props }) {
|
|
6
|
+
export function ErrorMessage({ className, tag: Tag = 'div', icon: Icon, errorTitle, message, ...props }) {
|
|
6
7
|
const styles = errorMessageStyles({});
|
|
7
8
|
const FinalIcon = Icon !== null && Icon !== void 0 ? Icon : AlertIcon;
|
|
9
|
+
if (errorTitle && Array.isArray(message)) {
|
|
10
|
+
return React.createElement("div", {
|
|
11
|
+
className: styles.titleWrapper({
|
|
12
|
+
className
|
|
13
|
+
}),
|
|
14
|
+
...props
|
|
15
|
+
}, React.createElement("span", {
|
|
16
|
+
className: styles.title({})
|
|
17
|
+
}, React.createElement(FinalIcon, {
|
|
18
|
+
color: "danger",
|
|
19
|
+
copyrightYear: "2026",
|
|
20
|
+
className: styles.icon({}),
|
|
21
|
+
size: "xsmall",
|
|
22
|
+
look: "outlined"
|
|
23
|
+
}), errorTitle), React.createElement(List, {
|
|
24
|
+
type: "bullet",
|
|
25
|
+
look: "primary",
|
|
26
|
+
className: styles.bulletList({})
|
|
27
|
+
}, message.map((msg, index)=>React.createElement(ListItem, {
|
|
28
|
+
key: index
|
|
29
|
+
}, msg))));
|
|
30
|
+
}
|
|
8
31
|
return Array.isArray(message) ? React.createElement("ul", {
|
|
9
32
|
className: styles.list({}),
|
|
10
33
|
...props
|
|
@@ -15,7 +38,7 @@ export function ErrorMessage({ className, tag: Tag = 'div', icon: Icon, message,
|
|
|
15
38
|
})
|
|
16
39
|
}, React.createElement(FinalIcon, {
|
|
17
40
|
color: "danger",
|
|
18
|
-
copyrightYear: "
|
|
41
|
+
copyrightYear: "2026",
|
|
19
42
|
className: styles.icon({}),
|
|
20
43
|
size: "xsmall",
|
|
21
44
|
look: "outlined"
|
|
@@ -26,7 +49,7 @@ export function ErrorMessage({ className, tag: Tag = 'div', icon: Icon, message,
|
|
|
26
49
|
...props
|
|
27
50
|
}, React.createElement(FinalIcon, {
|
|
28
51
|
color: "danger",
|
|
29
|
-
copyrightYear: "
|
|
52
|
+
copyrightYear: "2026",
|
|
30
53
|
className: styles.icon({}),
|
|
31
54
|
size: "xsmall",
|
|
32
55
|
look: "outlined"
|
|
@@ -2,36 +2,54 @@ export declare const styles: import("tailwind-variants").TVReturnType<{
|
|
|
2
2
|
[key: string]: {
|
|
3
3
|
[key: string]: import("tailwind-merge").ClassNameValue | {
|
|
4
4
|
base?: import("tailwind-merge").ClassNameValue;
|
|
5
|
+
title?: import("tailwind-merge").ClassNameValue;
|
|
5
6
|
list?: import("tailwind-merge").ClassNameValue;
|
|
6
7
|
icon?: import("tailwind-merge").ClassNameValue;
|
|
8
|
+
titleWrapper?: import("tailwind-merge").ClassNameValue;
|
|
9
|
+
bulletList?: import("tailwind-merge").ClassNameValue;
|
|
7
10
|
};
|
|
8
11
|
};
|
|
9
12
|
} | {
|
|
10
13
|
[x: string]: {
|
|
11
14
|
[x: string]: import("tailwind-merge").ClassNameValue | {
|
|
12
15
|
base?: import("tailwind-merge").ClassNameValue;
|
|
16
|
+
title?: import("tailwind-merge").ClassNameValue;
|
|
13
17
|
list?: import("tailwind-merge").ClassNameValue;
|
|
14
18
|
icon?: import("tailwind-merge").ClassNameValue;
|
|
19
|
+
titleWrapper?: import("tailwind-merge").ClassNameValue;
|
|
20
|
+
bulletList?: import("tailwind-merge").ClassNameValue;
|
|
15
21
|
};
|
|
16
22
|
};
|
|
17
23
|
} | {}, {
|
|
18
24
|
base: string;
|
|
19
25
|
list: string;
|
|
20
26
|
icon: string;
|
|
27
|
+
titleWrapper: string;
|
|
28
|
+
title: string;
|
|
29
|
+
bulletList: string;
|
|
21
30
|
}, undefined, {
|
|
22
31
|
[key: string]: {
|
|
23
32
|
[key: string]: import("tailwind-merge").ClassNameValue | {
|
|
24
33
|
base?: import("tailwind-merge").ClassNameValue;
|
|
34
|
+
title?: import("tailwind-merge").ClassNameValue;
|
|
25
35
|
list?: import("tailwind-merge").ClassNameValue;
|
|
26
36
|
icon?: import("tailwind-merge").ClassNameValue;
|
|
37
|
+
titleWrapper?: import("tailwind-merge").ClassNameValue;
|
|
38
|
+
bulletList?: import("tailwind-merge").ClassNameValue;
|
|
27
39
|
};
|
|
28
40
|
};
|
|
29
41
|
} | {}, {
|
|
30
42
|
base: string;
|
|
31
43
|
list: string;
|
|
32
44
|
icon: string;
|
|
45
|
+
titleWrapper: string;
|
|
46
|
+
title: string;
|
|
47
|
+
bulletList: string;
|
|
33
48
|
}, import("tailwind-variants").TVReturnType<unknown, {
|
|
34
49
|
base: string;
|
|
35
50
|
list: string;
|
|
36
51
|
icon: string;
|
|
52
|
+
titleWrapper: string;
|
|
53
|
+
title: string;
|
|
54
|
+
bulletList: string;
|
|
37
55
|
}, undefined, unknown, unknown, undefined>>;
|
|
@@ -3,6 +3,9 @@ export const styles = tv({
|
|
|
3
3
|
slots: {
|
|
4
4
|
base: 'flex items-start typography-body-11 text-text-danger',
|
|
5
5
|
list: 'mb-2 flex flex-col gap-1',
|
|
6
|
-
icon: 'mt-[0.25rem] mr-[0.5em] flex-shrink-0 align-top'
|
|
6
|
+
icon: 'mt-[0.25rem] mr-[0.5em] flex-shrink-0 align-top',
|
|
7
|
+
titleWrapper: 'mb-2 flex flex-col gap-1 text-text-danger',
|
|
8
|
+
title: 'flex items-start typography-body-11',
|
|
9
|
+
bulletList: 'text-text-danger'
|
|
7
10
|
}
|
|
8
11
|
});
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { type FieldProps } from './field.types.js';
|
|
2
|
-
export declare function Field({ className, label, tag: Tag, children, hintMessage, errorMessage, labelElementType, labelSize, ...props }: FieldProps): import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare function Field({ className, label, tag: Tag, children, hintMessage, errorTitle, errorMessage, labelElementType, labelSize, ...props }: FieldProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import React, { Children, cloneElement, isValidElement, useCallback } from 'react';
|
|
3
3
|
import { useField } from 'react-aria';
|
|
4
4
|
import { ErrorMessage, Hint, Label } from '../index.js';
|
|
5
|
-
export function Field({ className, label, tag: Tag = 'div', children, hintMessage, errorMessage, labelElementType, labelSize, ...props }) {
|
|
5
|
+
export function Field({ className, label, tag: Tag = 'div', children, hintMessage, errorTitle, errorMessage, labelElementType, labelSize, ...props }) {
|
|
6
6
|
const { labelProps, fieldProps, descriptionProps, errorMessageProps } = useField({
|
|
7
7
|
...props,
|
|
8
8
|
description: hintMessage,
|
|
@@ -28,6 +28,7 @@ export function Field({ className, label, tag: Tag = 'div', children, hintMessag
|
|
|
28
28
|
...labelProps
|
|
29
29
|
}, label), hintMessage && React.createElement(Hint, descriptionProps, hintMessage), errorMessage && React.createElement(ErrorMessage, {
|
|
30
30
|
...errorMessageProps,
|
|
31
|
+
errorTitle: errorTitle,
|
|
31
32
|
message: errorMessage
|
|
32
33
|
}), renderChildren());
|
|
33
34
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Icon } from '../icon.component.js';
|
|
3
|
+
export function FlagIcon({ look = 'filled', 'aria-label': ariaLabel = 'Flag', copyrightYear = '2026', ...props }) {
|
|
4
|
+
return React.createElement(Icon, {
|
|
5
|
+
"aria-label": ariaLabel,
|
|
6
|
+
copyrightYear: copyrightYear,
|
|
7
|
+
...props
|
|
8
|
+
}, look === 'filled' ? React.createElement("path", {
|
|
9
|
+
d: "M4 22V2H20L18 7L20 12H6V22H4Z",
|
|
10
|
+
fill: "currentColor"
|
|
11
|
+
}) : React.createElement("path", {
|
|
12
|
+
d: "M4 22V2H20L18 7L20 12H6V22H4ZM5.95 10H17L16 7L17.05 4H6L5.95 10Z",
|
|
13
|
+
fill: "currentColor"
|
|
14
|
+
}));
|
|
15
|
+
}
|
|
@@ -98,6 +98,7 @@ export { FilterIcon } from './components/filter-icon.js';
|
|
|
98
98
|
export { FingerprintIcon } from './components/fingerprint-icon.js';
|
|
99
99
|
export { FirstAidCaseIcon } from './components/first-aid-case-icon.js';
|
|
100
100
|
export { FirstAidIcon } from './components/first-aid-icon.js';
|
|
101
|
+
export { FlagIcon } from './components/flag-icon.js';
|
|
101
102
|
export { FormatColorIcon } from './components/format-color-icon.js';
|
|
102
103
|
export { FullscreenExitIcon } from './components/fullscreen-exit-icon.js';
|
|
103
104
|
export { FullscreenIcon } from './components/fullscreen-icon.js';
|
|
@@ -98,6 +98,7 @@ export { FilterIcon } from './components/filter-icon.js';
|
|
|
98
98
|
export { FingerprintIcon } from './components/fingerprint-icon.js';
|
|
99
99
|
export { FirstAidCaseIcon } from './components/first-aid-case-icon.js';
|
|
100
100
|
export { FirstAidIcon } from './components/first-aid-icon.js';
|
|
101
|
+
export { FlagIcon } from './components/flag-icon.js';
|
|
101
102
|
export { FormatColorIcon } from './components/format-color-icon.js';
|
|
102
103
|
export { FullscreenExitIcon } from './components/fullscreen-exit-icon.js';
|
|
103
104
|
export { FullscreenIcon } from './components/fullscreen-icon.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { SelectorButtonGroupContextState, SelectorButtonGroupProps } from './selector-button-group.types.js';
|
|
3
3
|
export declare const SelectorButtonContext: React.Context<SelectorButtonGroupContextState>;
|
|
4
|
-
export declare function SelectorButtonGroup({ className, children, label, orientation, errorMessage, description, value, isDisabled, ...props }: SelectorButtonGroupProps): import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
export declare function SelectorButtonGroup({ className, children, label, orientation, errorMessage, description, value, onChange, isDisabled, ...props }: SelectorButtonGroupProps): import("react/jsx-runtime").JSX.Element;
|
package/dist/components/selector/components/selector-button-group/selector-button-group.component.js
CHANGED
|
@@ -11,25 +11,34 @@ export const SelectorButtonContext = createContext({
|
|
|
11
11
|
validationState: 'valid',
|
|
12
12
|
isDisabled: undefined
|
|
13
13
|
});
|
|
14
|
-
export function SelectorButtonGroup({ className, children, label, orientation = 'vertical', errorMessage, description, value = '', isDisabled, ...props }) {
|
|
14
|
+
export function SelectorButtonGroup({ className, children, label, orientation = 'vertical', errorMessage, description, value = '', onChange, isDisabled, ...props }) {
|
|
15
|
+
const isControlled = onChange !== undefined;
|
|
16
|
+
const onChangeCallback = onChange;
|
|
15
17
|
const [selected, setSelected] = useState(value);
|
|
16
18
|
const breakpoint = useBreakpoint();
|
|
17
19
|
const resolvedOrientation = resolveResponsiveVariant(orientation, breakpoint);
|
|
18
20
|
const handleChange = useCallback((id)=>{
|
|
19
|
-
|
|
21
|
+
if (onChangeCallback) {
|
|
22
|
+
onChangeCallback(id);
|
|
23
|
+
} else {
|
|
24
|
+
setSelected(id);
|
|
25
|
+
}
|
|
20
26
|
}, [
|
|
27
|
+
onChangeCallback,
|
|
21
28
|
setSelected
|
|
22
29
|
]);
|
|
23
30
|
const state = useMemo(()=>({
|
|
24
|
-
value: selected,
|
|
31
|
+
value: isControlled ? value !== null && value !== void 0 ? value : '' : selected,
|
|
25
32
|
onClick: (id)=>handleChange(id),
|
|
26
33
|
validationState: errorMessage ? 'invalid' : 'valid',
|
|
27
34
|
isDisabled
|
|
28
35
|
}), [
|
|
29
36
|
errorMessage,
|
|
30
37
|
handleChange,
|
|
38
|
+
isControlled,
|
|
31
39
|
isDisabled,
|
|
32
|
-
selected
|
|
40
|
+
selected,
|
|
41
|
+
value
|
|
33
42
|
]);
|
|
34
43
|
const { labelProps, fieldProps, descriptionProps, errorMessageProps } = useField({
|
|
35
44
|
validationState: state.validationState,
|
package/dist/components/selector/components/selector-button-group/selector-button-group.types.d.ts
CHANGED
|
@@ -18,9 +18,14 @@ export type SelectorButtonGroupProps = {
|
|
|
18
18
|
*/
|
|
19
19
|
tag?: keyof JSX.IntrinsicElements;
|
|
20
20
|
/**
|
|
21
|
-
* Key to set as default value
|
|
21
|
+
* Key to set as default value (uncontrolled) or currently selected value (controlled)
|
|
22
22
|
*/
|
|
23
23
|
value?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Called when selection changes. Providing this prop makes the component controlled.
|
|
26
|
+
* Pass an empty string to clear the selection.
|
|
27
|
+
*/
|
|
28
|
+
onChange?: (value: string) => void;
|
|
24
29
|
} & AriaFieldProps & Omit<HTMLAttributes<Element>, 'onChange'>;
|
|
25
30
|
export type SelectorButtonGroupContextState = {
|
|
26
31
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@westpac/ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"type": "module",
|
|
@@ -254,8 +254,8 @@
|
|
|
254
254
|
"vite": "^7.1.12",
|
|
255
255
|
"vitest": "^3.2.4",
|
|
256
256
|
"@westpac/style-config": "~1.0.2",
|
|
257
|
-
"@westpac/test-config": "~0.0.0",
|
|
258
257
|
"@westpac/eslint-config": "~1.1.0",
|
|
258
|
+
"@westpac/test-config": "~0.0.0",
|
|
259
259
|
"@westpac/ts-config": "~0.0.0"
|
|
260
260
|
},
|
|
261
261
|
"dependencies": {
|
|
@@ -48,7 +48,6 @@ function Autocomplete<T extends object>(
|
|
|
48
48
|
}: AutocompleteProps<T>,
|
|
49
49
|
ref: ForwardedRef<HTMLInputElement>,
|
|
50
50
|
) {
|
|
51
|
-
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
52
51
|
const { contains } = useFilter({ sensitivity: 'base' });
|
|
53
52
|
const internalState = useComboBoxState({ isDisabled, ...props, defaultFilter: contains });
|
|
54
53
|
const state = comboBoxState ?? internalState;
|
|
@@ -97,19 +96,13 @@ function Autocomplete<T extends object>(
|
|
|
97
96
|
const outerRef = React.useRef(null);
|
|
98
97
|
|
|
99
98
|
const isNoOptionPopOverOpen = useMemo(() => {
|
|
99
|
+
const inputLength = searchProps.value?.length ?? 0;
|
|
100
100
|
return !!(
|
|
101
101
|
noOptionsMessage &&
|
|
102
|
-
((!state.isOpen && state.isFocused &&
|
|
103
|
-
(state.collection.size === 0 &&
|
|
102
|
+
((!state.isOpen && state.isFocused && inputLength > 0 && !state.value) ||
|
|
103
|
+
(state.collection.size === 0 && inputLength > 0))
|
|
104
104
|
);
|
|
105
|
-
}, [
|
|
106
|
-
noOptionsMessage,
|
|
107
|
-
state.isOpen,
|
|
108
|
-
state.isFocused,
|
|
109
|
-
state.selectedItems,
|
|
110
|
-
state.collection.size,
|
|
111
|
-
searchProps.value.length,
|
|
112
|
-
]);
|
|
105
|
+
}, [noOptionsMessage, state.isOpen, state.isFocused, state.value, state.collection.size, searchProps.value]);
|
|
113
106
|
|
|
114
107
|
return (
|
|
115
108
|
<div className={styles.base({ className })}>
|
|
@@ -2,26 +2,50 @@ import { clsx } from 'clsx';
|
|
|
2
2
|
import React, { ReactNode } from 'react';
|
|
3
3
|
|
|
4
4
|
import { AlertIcon } from '../../components/icon/index.js';
|
|
5
|
+
import { List, ListItem } from '../../components/list/index.js';
|
|
5
6
|
|
|
6
7
|
import { styles as errorMessageStyles } from './error-message.styles.js';
|
|
7
8
|
import { type ErrorMessageProps } from './error-message.types.js';
|
|
8
9
|
|
|
9
|
-
export function ErrorMessage({
|
|
10
|
+
export function ErrorMessage({
|
|
11
|
+
className,
|
|
12
|
+
tag: Tag = 'div',
|
|
13
|
+
icon: Icon,
|
|
14
|
+
errorTitle,
|
|
15
|
+
message,
|
|
16
|
+
...props
|
|
17
|
+
}: ErrorMessageProps) {
|
|
10
18
|
const styles = errorMessageStyles({});
|
|
11
19
|
const FinalIcon = Icon ?? AlertIcon;
|
|
12
20
|
|
|
21
|
+
if (errorTitle && Array.isArray(message)) {
|
|
22
|
+
return (
|
|
23
|
+
<div className={styles.titleWrapper({ className })} {...props}>
|
|
24
|
+
<span className={styles.title({})}>
|
|
25
|
+
<FinalIcon color="danger" copyrightYear="2026" className={styles.icon({})} size="xsmall" look="outlined" />
|
|
26
|
+
{errorTitle}
|
|
27
|
+
</span>
|
|
28
|
+
<List type="bullet" look="primary" className={styles.bulletList({})}>
|
|
29
|
+
{message.map((msg, index) => (
|
|
30
|
+
<ListItem key={index}>{msg}</ListItem>
|
|
31
|
+
))}
|
|
32
|
+
</List>
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
13
37
|
return Array.isArray(message) ? (
|
|
14
38
|
<ul className={styles.list({})} {...props}>
|
|
15
39
|
{message.map((msg, index) => (
|
|
16
40
|
<li key={index} className={styles.base({ className })}>
|
|
17
|
-
<FinalIcon color="danger" copyrightYear="
|
|
41
|
+
<FinalIcon color="danger" copyrightYear="2026" className={styles.icon({})} size="xsmall" look="outlined" />
|
|
18
42
|
{msg}
|
|
19
43
|
</li>
|
|
20
44
|
))}
|
|
21
45
|
</ul>
|
|
22
46
|
) : (
|
|
23
47
|
<Tag className={styles.base({ className: clsx(className, 'mb-2') })} {...props}>
|
|
24
|
-
<FinalIcon color="danger" copyrightYear="
|
|
48
|
+
<FinalIcon color="danger" copyrightYear="2026" className={styles.icon({})} size="xsmall" look="outlined" />
|
|
25
49
|
{message as ReactNode}
|
|
26
50
|
</Tag>
|
|
27
51
|
);
|
|
@@ -6,5 +6,8 @@ export const styles = tv({
|
|
|
6
6
|
list: 'mb-2 flex flex-col gap-1',
|
|
7
7
|
// below should be em rather than rem based on old GEL
|
|
8
8
|
icon: 'mt-[0.25rem] mr-[0.5em] flex-shrink-0 align-top',
|
|
9
|
+
titleWrapper: 'mb-2 flex flex-col gap-1 text-text-danger',
|
|
10
|
+
title: 'flex items-start typography-body-11',
|
|
11
|
+
bulletList: 'text-text-danger',
|
|
9
12
|
},
|
|
10
13
|
});
|
|
@@ -13,6 +13,7 @@ export function Field({
|
|
|
13
13
|
tag: Tag = 'div',
|
|
14
14
|
children,
|
|
15
15
|
hintMessage,
|
|
16
|
+
errorTitle,
|
|
16
17
|
errorMessage,
|
|
17
18
|
labelElementType,
|
|
18
19
|
labelSize,
|
|
@@ -42,7 +43,7 @@ export function Field({
|
|
|
42
43
|
</Label>
|
|
43
44
|
)}
|
|
44
45
|
{hintMessage && <Hint {...descriptionProps}>{hintMessage}</Hint>}
|
|
45
|
-
{errorMessage && <ErrorMessage {...errorMessageProps} message={errorMessage} />}
|
|
46
|
+
{errorMessage && <ErrorMessage {...errorMessageProps} errorTitle={errorTitle} message={errorMessage} />}
|
|
46
47
|
{renderChildren()}
|
|
47
48
|
</Tag>
|
|
48
49
|
);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import { Icon } from '../icon.component.js';
|
|
4
|
+
import { type IconProps } from '../icon.types.js';
|
|
5
|
+
|
|
6
|
+
export function FlagIcon({
|
|
7
|
+
look = 'filled',
|
|
8
|
+
'aria-label': ariaLabel = 'Flag',
|
|
9
|
+
copyrightYear = '2026',
|
|
10
|
+
...props
|
|
11
|
+
}: IconProps) {
|
|
12
|
+
return (
|
|
13
|
+
<Icon aria-label={ariaLabel} copyrightYear={copyrightYear} {...props}>
|
|
14
|
+
{look === 'filled' ? (
|
|
15
|
+
<path d="M4 22V2H20L18 7L20 12H6V22H4Z" fill="currentColor" />
|
|
16
|
+
) : (
|
|
17
|
+
<path d="M4 22V2H20L18 7L20 12H6V22H4ZM5.95 10H17L16 7L17.05 4H6L5.95 10Z" fill="currentColor" />
|
|
18
|
+
)}
|
|
19
|
+
</Icon>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -98,6 +98,7 @@ export { FilterIcon } from './components/filter-icon.js';
|
|
|
98
98
|
export { FingerprintIcon } from './components/fingerprint-icon.js';
|
|
99
99
|
export { FirstAidCaseIcon } from './components/first-aid-case-icon.js';
|
|
100
100
|
export { FirstAidIcon } from './components/first-aid-icon.js';
|
|
101
|
+
export { FlagIcon } from './components/flag-icon.js';
|
|
101
102
|
export { FormatColorIcon } from './components/format-color-icon.js';
|
|
102
103
|
export { FullscreenExitIcon } from './components/fullscreen-exit-icon.js';
|
|
103
104
|
export { FullscreenIcon } from './components/fullscreen-icon.js';
|
package/src/components/selector/components/selector-button-group/selector-button-group.component.tsx
CHANGED
|
@@ -25,28 +25,35 @@ export function SelectorButtonGroup({
|
|
|
25
25
|
errorMessage,
|
|
26
26
|
description,
|
|
27
27
|
value = '',
|
|
28
|
+
onChange,
|
|
28
29
|
isDisabled,
|
|
29
30
|
...props
|
|
30
31
|
}: SelectorButtonGroupProps) {
|
|
32
|
+
const isControlled = onChange !== undefined;
|
|
33
|
+
const onChangeCallback = onChange as ((value: string) => void) | undefined;
|
|
31
34
|
const [selected, setSelected] = useState(value);
|
|
32
35
|
const breakpoint = useBreakpoint();
|
|
33
36
|
const resolvedOrientation = resolveResponsiveVariant(orientation, breakpoint);
|
|
34
37
|
|
|
35
38
|
const handleChange = useCallback(
|
|
36
39
|
(id: string) => {
|
|
37
|
-
|
|
40
|
+
if (onChangeCallback) {
|
|
41
|
+
onChangeCallback(id);
|
|
42
|
+
} else {
|
|
43
|
+
setSelected(id);
|
|
44
|
+
}
|
|
38
45
|
},
|
|
39
|
-
[setSelected],
|
|
46
|
+
[onChangeCallback, setSelected],
|
|
40
47
|
);
|
|
41
48
|
|
|
42
49
|
const state: SelectorButtonGroupContextState = useMemo(
|
|
43
50
|
() => ({
|
|
44
|
-
value: selected,
|
|
51
|
+
value: isControlled ? (value ?? '') : selected,
|
|
45
52
|
onClick: (id: string) => handleChange(id),
|
|
46
53
|
validationState: errorMessage ? 'invalid' : 'valid',
|
|
47
54
|
isDisabled,
|
|
48
55
|
}),
|
|
49
|
-
[errorMessage, handleChange, isDisabled, selected],
|
|
56
|
+
[errorMessage, handleChange, isControlled, isDisabled, selected, value],
|
|
50
57
|
);
|
|
51
58
|
|
|
52
59
|
const { labelProps, fieldProps, descriptionProps, errorMessageProps } = useField({
|
package/src/components/selector/components/selector-button-group/selector-button-group.types.ts
CHANGED
|
@@ -22,9 +22,14 @@ export type SelectorButtonGroupProps = {
|
|
|
22
22
|
*/
|
|
23
23
|
tag?: keyof JSX.IntrinsicElements;
|
|
24
24
|
/**
|
|
25
|
-
* Key to set as default value
|
|
25
|
+
* Key to set as default value (uncontrolled) or currently selected value (controlled)
|
|
26
26
|
*/
|
|
27
27
|
value?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Called when selection changes. Providing this prop makes the component controlled.
|
|
30
|
+
* Pass an empty string to clear the selection.
|
|
31
|
+
*/
|
|
32
|
+
onChange?: (value: string) => void;
|
|
28
33
|
} & AriaFieldProps &
|
|
29
34
|
Omit<HTMLAttributes<Element>, 'onChange'>;
|
|
30
35
|
|