@reykjavik/hanna-react 0.10.145 → 0.10.147

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/CHANGELOG.md CHANGED
@@ -4,6 +4,22 @@
4
4
 
5
5
  - ... <!-- Add new lines here. -->
6
6
 
7
+ ## 0.10.147
8
+
9
+ _2025-02-25_
10
+
11
+ - `AutosuggestSearch`:
12
+ - feat: Add props `errorMessage`, `invalid`, `assistText`
13
+ - feat: Add props `onInputFocus` and `onInputBlur`
14
+ - fix: `wrapperProps.className` overrides wrapper's default class
15
+ - fix: Make `onClearOptions` prop optional
16
+
17
+ ## 0.10.146
18
+
19
+ _2025-02-10_
20
+
21
+ - fix: Avoid conrolled–uncontrolled warning for `Datepicker` in `isoMode`
22
+
7
23
  ## 0.10.145
8
24
 
9
25
  _2025-01-14_
package/Datepicker.js CHANGED
@@ -110,7 +110,7 @@ const defaultDatepickerTexts = {
110
110
  };
111
111
  const toLocalIsoDate = (date) => {
112
112
  if (!date) {
113
- return undefined;
113
+ return '';
114
114
  }
115
115
  const localDate = new Date(date.getTime() - date.getTimezoneOffset() * 60000);
116
116
  return localDate.toISOString().split('T')[0];
@@ -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
  };
package/esm/Datepicker.js CHANGED
@@ -105,7 +105,7 @@ const defaultDatepickerTexts = {
105
105
  };
106
106
  const toLocalIsoDate = (date) => {
107
107
  if (!date) {
108
- return undefined;
108
+ return '';
109
109
  }
110
110
  const localDate = new Date(date.getTime() - date.getTimezoneOffset() * 60000);
111
111
  return localDate.toISOString().split('T')[0];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reykjavik/hanna-react",
3
- "version": "0.10.145",
3
+ "version": "0.10.147",
4
4
  "author": "Reykjavík (http://www.reykjavik.is)",
5
5
  "contributors": [
6
6
  "Hugsmiðjan ehf (http://www.hugsmidjan.is)",