@reykjavik/hanna-react 0.10.146 → 0.10.148

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.
@@ -30,7 +30,23 @@ export type AutosuggestSearchProps<T extends string | object> = {
30
30
  value: string;
31
31
  option: T;
32
32
  }) => void;
33
- onClearOptions: () => void;
33
+ onClearOptions?: () => void;
34
+ /**
35
+ * Called when the sarch input receives focus, except when the user clicks
36
+ * a suggestion.
37
+ *
38
+ * Clicking a suggestion briefly blurs the input, and then when the
39
+ * input is immediately re-focused, this event is not triggered.
40
+ */
41
+ onInputFocus?: () => void;
42
+ /**
43
+ * Called when the search input loses focus, except when the user clicks
44
+ * a suggestion and the input is immediately re-focused.
45
+ *
46
+ * Thus the triggering of this callback is thus always delayed by about 100ms
47
+ * and may occur after other elements have received focused.
48
+ */
49
+ onInputBlurred?: () => void;
34
50
  getOptionValue?: (option: T) => string;
35
51
  renderSuggestion?: (option: T, context: {
36
52
  query: string;
@@ -43,6 +59,6 @@ export type AutosuggestSearchProps<T extends string | object> = {
43
59
  renderInputField?: (inputProps: RenderInputComponentProps, texts: AutosuggestSearchI18n) => JSX.Element;
44
60
  texts?: AutosuggestSearchI18n;
45
61
  lang?: HannaLang;
46
- } & Pick<SearchInputProps, 'onSubmit' | 'onButtonClick' | 'button'> & WrapperElmProps;
62
+ } & Pick<SearchInputProps, 'onSubmit' | 'onButtonClick' | 'button' | 'invalid' | 'errorMessage' | 'assistText'> & WrapperElmProps<'div', 'aria-label'>;
47
63
  export declare const AutosuggestSearch: <T extends string | object>(props: AutosuggestSearchProps<T>) => JSX.Element;
48
64
  export {};
@@ -40,20 +40,40 @@ const AutosuggestSearch = (props) => {
40
40
  ? opt.value.toString()
41
41
  : '', renderSuggestion = (opt) => typeof opt === 'object' && 'label' in opt && opt.label != null
42
42
  ? opt.label.toString()
43
- : getOptionValue(opt), InputComponent = SearchInput_js_1.default, renderInputField, wrapperProps, } = props;
43
+ : getOptionValue(opt), InputComponent = SearchInput_js_1.default, renderInputField, invalid, errorMessage, assistText, onInputFocus, onInputBlurred, } = props;
44
44
  const [inputValue, setInputValue] = (0, utils_js_1.useMixedControlState)(props, 'inputValue', '');
45
45
  const inputRef = (0, react_1.useRef)(null);
46
46
  const txt = (0, i18n_1.getTexts)(props, exports.defaultAutosuggestSearchTexts);
47
47
  const showEmptyMessage = !options.length && emptyMessage;
48
+ const _a = props.wrapperProps || {}, { className } = _a, wrapperProps = tslib_1.__rest(_a, ["className"]);
49
+ const blurTimeout = (0, react_1.useRef)();
50
+ const inputFocusProps = onInputFocus || onInputBlurred
51
+ ? {
52
+ onFocus: () => {
53
+ if (!blurTimeout.current) {
54
+ onInputFocus === null || onInputFocus === void 0 ? void 0 : onInputFocus();
55
+ }
56
+ clearTimeout(blurTimeout.current);
57
+ blurTimeout.current = undefined;
58
+ },
59
+ onBlur: () => {
60
+ clearTimeout(blurTimeout.current);
61
+ blurTimeout.current = setTimeout(() => {
62
+ blurTimeout.current = undefined;
63
+ onInputBlurred === null || onInputBlurred === void 0 ? void 0 : onInputBlurred();
64
+ }, 100);
65
+ },
66
+ }
67
+ : undefined;
48
68
  return (react_1.default.createElement(react_autosuggest_1.default, { theme: {
49
- container: 'AutosuggestSearch',
69
+ container: `AutosuggestSearch ${className || ''}`,
50
70
  containerOpen: 'AutosuggestSearch--open',
51
71
  suggestionsContainer: 'AutosuggestSearch__container',
52
72
  suggestionsContainerOpen: 'AutosuggestSearch__container--open',
53
73
  suggestionsList: (0, hanna_utils_1.modifiedClass)('AutosuggestSearch__list', itemActionIcon && `action--${itemActionIcon}`),
54
74
  suggestion: 'AutosuggestSearch__item',
55
75
  suggestionHighlighted: 'AutosuggestSearch__item--highlighted',
56
- }, focusInputOnSuggestionClick: true, suggestions: showEmptyMessage ? [true] : options, onSuggestionsClearRequested: onClearOptions, onSuggestionsFetchRequested: ( /* { value } */) => {
76
+ }, focusInputOnSuggestionClick: true, suggestions: showEmptyMessage ? [true] : options, onSuggestionsClearRequested: onClearOptions || (() => undefined), onSuggestionsFetchRequested: ( /* { value } */) => {
57
77
  // weirdly required prop, but we don't need to do anything here
58
78
  // as we run onInput on input change below.
59
79
  }, getSuggestionValue: showEmptyMessage
@@ -75,22 +95,18 @@ const AutosuggestSearch = (props) => {
75
95
  contents = (react_1.default.createElement("div", { className: (0, hanna_utils_1.modifiedClass)('AutosuggestSearch__emptyMessage', type !== 'empty' && type) }, message));
76
96
  }
77
97
  return (react_1.default.createElement("div", Object.assign({}, containerProps, { "aria-label": options.length ? txt.suggestionsLabel : undefined }), contents));
78
- }, inputProps: {
79
- ref: inputRef,
80
- value: inputValue,
81
- onChange: (_, { newValue, method }) => {
98
+ }, inputProps: Object.assign({ ref: inputRef, value: inputValue, onChange: (_, { newValue, method }) => {
82
99
  if (!inputChangeMethods.has(method)) {
83
100
  return;
84
101
  }
85
102
  onInput(newValue);
86
103
  setInputValue(newValue);
87
- },
88
- }, renderInputComponent: renderInputField
104
+ } }, inputFocusProps), renderInputComponent: renderInputField
89
105
  ? (inputProps) => renderInputField(inputProps, txt)
90
106
  : (inputProps) => {
91
107
  /* prettier-ignore */
92
108
  const { className, type, disabled, readOnly, required, children, ref, defaultValue } = inputProps, siteSearchProps = tslib_1.__rest(inputProps, ["className", "type", "disabled", "readOnly", "required", "children", "ref", "defaultValue"]);
93
- return (react_1.default.createElement(InputComponent, Object.assign({ lang: props.lang, defaultValue: defaultValue }, siteSearchProps, { inputRef: ref, button: button, label: txt.inputLabel, placeholder: txt.placeholder, buttonText: txt.buttonText, onSubmit: onSubmit && (() => onSubmit(inputValue)), onButtonClick: onButtonClick && (() => onButtonClick(inputValue)) })));
109
+ return (react_1.default.createElement(InputComponent, Object.assign({ lang: props.lang, defaultValue: defaultValue }, siteSearchProps, { inputRef: ref, button: button, label: txt.inputLabel, placeholder: txt.placeholder, buttonText: txt.buttonText, invalid: invalid, errorMessage: errorMessage, assistText: assistText, onSubmit: onSubmit && (() => onSubmit(inputValue)), onButtonClick: onButtonClick && (() => onButtonClick(inputValue)) })));
94
110
  } }));
95
111
  };
96
112
  exports.AutosuggestSearch = AutosuggestSearch;
package/BreadCrumbs.js CHANGED
@@ -32,7 +32,7 @@ const BreadCrumbs = (props) => {
32
32
  return (react_1.default.createElement(react_1.default.Fragment, { key: i },
33
33
  react_1.default.createElement(BreadCrumbs__item, { link: link }),
34
34
  ' ',
35
- react_1.default.createElement("span", { className: "BreadCrumbs__separator", "aria-label": "" }, ">"),
35
+ react_1.default.createElement("span", { className: "BreadCrumbs__separator", role: "none" }, ">"),
36
36
  ' '));
37
37
  }),
38
38
  react_1.default.createElement(BreadCrumbs__item, { link: current, current: true })));
package/CHANGELOG.md CHANGED
@@ -4,6 +4,23 @@
4
4
 
5
5
  - ... <!-- Add new lines here. -->
6
6
 
7
+ ## 0.10.148
8
+
9
+ _2025-03-26_
10
+
11
+ - `BreadCrumbs`:
12
+ - fix: Drop `aria-lebel=""` for `role="none"` on `BreadCrumbs__separator`
13
+
14
+ ## 0.10.147
15
+
16
+ _2025-02-25_
17
+
18
+ - `AutosuggestSearch`:
19
+ - feat: Add props `errorMessage`, `invalid`, `assistText`
20
+ - feat: Add props `onInputFocus` and `onInputBlur`
21
+ - fix: `wrapperProps.className` overrides wrapper's default class
22
+ - fix: Make `onClearOptions` prop optional
23
+
7
24
  ## 0.10.146
8
25
 
9
26
  _2025-02-10_
@@ -30,7 +30,23 @@ export type AutosuggestSearchProps<T extends string | object> = {
30
30
  value: string;
31
31
  option: T;
32
32
  }) => void;
33
- onClearOptions: () => void;
33
+ onClearOptions?: () => void;
34
+ /**
35
+ * Called when the sarch input receives focus, except when the user clicks
36
+ * a suggestion.
37
+ *
38
+ * Clicking a suggestion briefly blurs the input, and then when the
39
+ * input is immediately re-focused, this event is not triggered.
40
+ */
41
+ onInputFocus?: () => void;
42
+ /**
43
+ * Called when the search input loses focus, except when the user clicks
44
+ * a suggestion and the input is immediately re-focused.
45
+ *
46
+ * Thus the triggering of this callback is thus always delayed by about 100ms
47
+ * and may occur after other elements have received focused.
48
+ */
49
+ onInputBlurred?: () => void;
34
50
  getOptionValue?: (option: T) => string;
35
51
  renderSuggestion?: (option: T, context: {
36
52
  query: string;
@@ -43,6 +59,6 @@ export type AutosuggestSearchProps<T extends string | object> = {
43
59
  renderInputField?: (inputProps: RenderInputComponentProps, texts: AutosuggestSearchI18n) => JSX.Element;
44
60
  texts?: AutosuggestSearchI18n;
45
61
  lang?: HannaLang;
46
- } & Pick<SearchInputProps, 'onSubmit' | 'onButtonClick' | 'button'> & WrapperElmProps;
62
+ } & Pick<SearchInputProps, 'onSubmit' | 'onButtonClick' | 'button' | 'invalid' | 'errorMessage' | 'assistText'> & WrapperElmProps<'div', 'aria-label'>;
47
63
  export declare const AutosuggestSearch: <T extends string | object>(props: AutosuggestSearchProps<T>) => JSX.Element;
48
64
  export {};
@@ -37,20 +37,40 @@ export const AutosuggestSearch = (props) => {
37
37
  ? opt.value.toString()
38
38
  : '', renderSuggestion = (opt) => typeof opt === 'object' && 'label' in opt && opt.label != null
39
39
  ? opt.label.toString()
40
- : getOptionValue(opt), InputComponent = SearchInput, renderInputField, wrapperProps, } = props;
40
+ : getOptionValue(opt), InputComponent = SearchInput, renderInputField, invalid, errorMessage, assistText, onInputFocus, onInputBlurred, } = props;
41
41
  const [inputValue, setInputValue] = useMixedControlState(props, 'inputValue', '');
42
42
  const inputRef = useRef(null);
43
43
  const txt = getTexts(props, defaultAutosuggestSearchTexts);
44
44
  const showEmptyMessage = !options.length && emptyMessage;
45
+ const _a = props.wrapperProps || {}, { className } = _a, wrapperProps = __rest(_a, ["className"]);
46
+ const blurTimeout = useRef();
47
+ const inputFocusProps = onInputFocus || onInputBlurred
48
+ ? {
49
+ onFocus: () => {
50
+ if (!blurTimeout.current) {
51
+ onInputFocus === null || onInputFocus === void 0 ? void 0 : onInputFocus();
52
+ }
53
+ clearTimeout(blurTimeout.current);
54
+ blurTimeout.current = undefined;
55
+ },
56
+ onBlur: () => {
57
+ clearTimeout(blurTimeout.current);
58
+ blurTimeout.current = setTimeout(() => {
59
+ blurTimeout.current = undefined;
60
+ onInputBlurred === null || onInputBlurred === void 0 ? void 0 : onInputBlurred();
61
+ }, 100);
62
+ },
63
+ }
64
+ : undefined;
45
65
  return (React.createElement(Autosuggest, { theme: {
46
- container: 'AutosuggestSearch',
66
+ container: `AutosuggestSearch ${className || ''}`,
47
67
  containerOpen: 'AutosuggestSearch--open',
48
68
  suggestionsContainer: 'AutosuggestSearch__container',
49
69
  suggestionsContainerOpen: 'AutosuggestSearch__container--open',
50
70
  suggestionsList: modifiedClass('AutosuggestSearch__list', itemActionIcon && `action--${itemActionIcon}`),
51
71
  suggestion: 'AutosuggestSearch__item',
52
72
  suggestionHighlighted: 'AutosuggestSearch__item--highlighted',
53
- }, focusInputOnSuggestionClick: true, suggestions: showEmptyMessage ? [true] : options, onSuggestionsClearRequested: onClearOptions, onSuggestionsFetchRequested: ( /* { value } */) => {
73
+ }, focusInputOnSuggestionClick: true, suggestions: showEmptyMessage ? [true] : options, onSuggestionsClearRequested: onClearOptions || (() => undefined), onSuggestionsFetchRequested: ( /* { value } */) => {
54
74
  // weirdly required prop, but we don't need to do anything here
55
75
  // as we run onInput on input change below.
56
76
  }, getSuggestionValue: showEmptyMessage
@@ -72,21 +92,17 @@ export const AutosuggestSearch = (props) => {
72
92
  contents = (React.createElement("div", { className: modifiedClass('AutosuggestSearch__emptyMessage', type !== 'empty' && type) }, message));
73
93
  }
74
94
  return (React.createElement("div", Object.assign({}, containerProps, { "aria-label": options.length ? txt.suggestionsLabel : undefined }), contents));
75
- }, inputProps: {
76
- ref: inputRef,
77
- value: inputValue,
78
- onChange: (_, { newValue, method }) => {
95
+ }, inputProps: Object.assign({ ref: inputRef, value: inputValue, onChange: (_, { newValue, method }) => {
79
96
  if (!inputChangeMethods.has(method)) {
80
97
  return;
81
98
  }
82
99
  onInput(newValue);
83
100
  setInputValue(newValue);
84
- },
85
- }, renderInputComponent: renderInputField
101
+ } }, inputFocusProps), renderInputComponent: renderInputField
86
102
  ? (inputProps) => renderInputField(inputProps, txt)
87
103
  : (inputProps) => {
88
104
  /* prettier-ignore */
89
105
  const { className, type, disabled, readOnly, required, children, ref, defaultValue } = inputProps, siteSearchProps = __rest(inputProps, ["className", "type", "disabled", "readOnly", "required", "children", "ref", "defaultValue"]);
90
- return (React.createElement(InputComponent, Object.assign({ lang: props.lang, defaultValue: defaultValue }, siteSearchProps, { inputRef: ref, button: button, label: txt.inputLabel, placeholder: txt.placeholder, buttonText: txt.buttonText, onSubmit: onSubmit && (() => onSubmit(inputValue)), onButtonClick: onButtonClick && (() => onButtonClick(inputValue)) })));
106
+ return (React.createElement(InputComponent, Object.assign({ lang: props.lang, defaultValue: defaultValue }, siteSearchProps, { inputRef: ref, button: button, label: txt.inputLabel, placeholder: txt.placeholder, buttonText: txt.buttonText, invalid: invalid, errorMessage: errorMessage, assistText: assistText, onSubmit: onSubmit && (() => onSubmit(inputValue)), onButtonClick: onButtonClick && (() => onButtonClick(inputValue)) })));
91
107
  } }));
92
108
  };
@@ -28,7 +28,7 @@ export const BreadCrumbs = (props) => {
28
28
  return (React.createElement(React.Fragment, { key: i },
29
29
  React.createElement(BreadCrumbs__item, { link: link }),
30
30
  ' ',
31
- React.createElement("span", { className: "BreadCrumbs__separator", "aria-label": "" }, ">"),
31
+ React.createElement("span", { className: "BreadCrumbs__separator", role: "none" }, ">"),
32
32
  ' '));
33
33
  }),
34
34
  React.createElement(BreadCrumbs__item, { link: current, current: true })));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reykjavik/hanna-react",
3
- "version": "0.10.146",
3
+ "version": "0.10.148",
4
4
  "author": "Reykjavík (http://www.reykjavik.is)",
5
5
  "contributors": [
6
6
  "Hugsmiðjan ehf (http://www.hugsmidjan.is)",