@jobber/components 2.79.0 → 2.80.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 CHANGED
@@ -8,6 +8,17 @@ menu: Changelog
8
8
  All notable changes to this project will be documented in this file.
9
9
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
10
10
 
11
+ # [2.80.0](https://github.com/GetJobber/atlantis/compare/@jobber/components@2.79.0...@jobber/components@2.80.0) (2022-04-05)
12
+
13
+
14
+ ### Features
15
+
16
+ * **components:** Enable chip navigation with left and right arrow keys ([#872](https://github.com/GetJobber/atlantis/issues/872)) ([065b17b](https://github.com/GetJobber/atlantis/commit/065b17bf55161c85d11346fdd1d41a1c6e01e39d))
17
+
18
+
19
+
20
+
21
+
11
22
  # [2.79.0](https://github.com/GetJobber/atlantis/compare/@jobber/components@2.78.3...@jobber/components@2.79.0) (2022-03-25)
12
23
 
13
24
 
@@ -1,4 +1,4 @@
1
- import { MouseEvent, ReactElement } from "react";
1
+ import { KeyboardEvent, MouseEvent, ReactElement } from "react";
2
2
  import { ChipButtonProps } from "./InternalChipButton";
3
3
  import { AvatarProps } from "../Avatar";
4
4
  import { IconProps } from "../Icon";
@@ -12,7 +12,7 @@ export interface InternalChipProps {
12
12
  */
13
13
  readonly active?: boolean;
14
14
  /**
15
- * Makes the chip look and feels uninteractable.
15
+ * Makes the chip look and feel uninteractable.
16
16
  */
17
17
  readonly disabled?: boolean;
18
18
  /**
@@ -35,8 +35,25 @@ export interface InternalChipProps {
35
35
  * Adds a component on the right side of the label.
36
36
  */
37
37
  readonly suffix?: ReactElement<IconProps | ChipButtonProps>;
38
+ /**
39
+ * The label for the interactive chip element for voice over
40
+ */
41
+ readonly ariaLabel?: string;
42
+ /**
43
+ * Allows the chip to be focused.
44
+ *
45
+ * **NOTE**: Clickable chips are focusable by default so you don't need to
46
+ * specify this prop.
47
+ *
48
+ * This has the same effect as HTML [tabindex](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex).
49
+ */
50
+ readonly tabIndex?: number;
38
51
  /**
39
52
  * Callback when the chip itself gets clicked.
40
53
  */
41
54
  onClick?(event: MouseEvent<HTMLDivElement | HTMLInputElement | HTMLButtonElement>): void;
55
+ /**
56
+ * Callback for keyboard interaction with chips (e.g., chip deletion).
57
+ */
58
+ onKeyDown?(event: KeyboardEvent<HTMLDivElement | HTMLButtonElement>): void;
42
59
  }
@@ -1,2 +1,2 @@
1
1
  import { InternalChipProps } from "./ChipTypes";
2
- export declare function InternalChip({ label, active, disabled, invalid, prefix, suffix, onClick, }: InternalChipProps): JSX.Element;
2
+ export declare function InternalChip({ label, active, disabled, invalid, prefix, suffix, tabIndex, ariaLabel, onClick, onKeyDown, }: InternalChipProps): JSX.Element;
@@ -5,7 +5,6 @@ export declare type InternalChipDismissibleProps = Omit<ChipDismissibleProps, "t
5
5
  export interface ChipDismissibleInputProps extends Pick<InternalChipDismissibleProps, "activator" | "isLoadingMore" | "onSearch" | "onLoadMore"> {
6
6
  readonly attachTo: React.RefObject<Element | null>;
7
7
  readonly options: ChipProps[];
8
- onEmptyBackspace(): void;
9
8
  onCustomOptionSelect?(value: string): void;
10
9
  onOptionSelect(value: string): void;
11
10
  }
@@ -1,12 +1,15 @@
1
- import { MouseEvent } from "react";
1
+ import { KeyboardEvent, MouseEvent } from "react";
2
2
  import { InternalChipDismissibleProps } from "../InternalChipDismissibleTypes";
3
3
  export declare function useInternalChipDismissible({ children, selected, onChange, onClick, onCustomAdd, }: InternalChipDismissibleProps): {
4
4
  ref: import("react").RefObject<HTMLDivElement>;
5
5
  sortedVisibleChipOptions: import("../../Chip").ChipProps[];
6
6
  availableChipOptions: import("../../Chip").ChipProps[];
7
- handleChipRemove: (chip: import("../../Chip").ChipProps) => () => void;
7
+ handleChipRemove: (value: string) => () => void;
8
8
  handleChipAdd: (value: string) => void;
9
9
  handleCustomAdd: ((value: string) => void) | undefined;
10
10
  handleChipClick: (value: string) => ((event: MouseEvent<HTMLButtonElement, globalThis.MouseEvent>) => void) | undefined;
11
- handleEmptyBackspace: () => void;
11
+ handleWrapperKeyDown: (event: KeyboardEvent<HTMLDivElement> & {
12
+ target: HTMLElement;
13
+ }) => void;
14
+ handleChipKeyDown: (value: string) => (event: KeyboardEvent<HTMLElement>) => void;
12
15
  };
@@ -1,7 +1,7 @@
1
1
  /// <reference types="lodash" />
2
2
  import React from "react";
3
3
  import { ChipDismissibleInputOptionProps, ChipDismissibleInputProps } from "../InternalChipDismissibleTypes";
4
- export declare function useInternalChipDismissibleInput({ options, isLoadingMore, onEmptyBackspace, onCustomOptionSelect, onOptionSelect, onSearch, }: ChipDismissibleInputProps): {
4
+ export declare function useInternalChipDismissibleInput({ options, isLoadingMore, onCustomOptionSelect, onOptionSelect, onSearch, }: ChipDismissibleInputProps): {
5
5
  menuOpen: boolean;
6
6
  searchValue: string;
7
7
  shouldCancelBlur: boolean;
@@ -26,7 +26,7 @@ var index$4 = require('../Tooltip/index.js');
26
26
  var reactPopper = require('react-popper');
27
27
  var lodash = require('lodash');
28
28
 
29
- var css = "/**\n * Used in the wrapping chips component:\n * - InternalChipSingleSelect\n * - InternalChipMultiSelect\n */\n\n.uhcyg-a-GLRKF_7oHwvMP {\n display: -ms-flexbox;\n display: flex;\n grid-gap: var(--space-small);\n grid-gap: var(--space-small);\n gap: var(--space-small);\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n -ms-flex-align: center;\n align-items: center;\n}\n\n._2XfthED8eqbKIyTNoH1aWp {\n display: inline-grid;\n position: relative;\n max-width: 100%;\n box-sizing: border-box;\n padding: var(--space-small);\n border: var(--border-base) solid var(--color-border);\n border-radius: var(--radius-larger);\n color: var(--color-text--secondary);\n text-align: left;\n vertical-align: middle;\n background-color: var(--color-surface);\n transition: var(--timing-base) ease-in-out;\n transition-property: background-color, box-shadow, color;\n grid-template-columns: -webkit-max-content;\n grid-template-columns: max-content;\n -ms-flex-align: center;\n align-items: center;\n grid-gap: var(--space-small);\n grid-gap: var(--space-small);\n gap: var(--space-small);\n}\n\n._4U2lNr3Kk6xnNZTk3Fxgm,\n.fl19i7ccgBu7NZkxnpvcI {\n padding: calc(var(--space-smaller) + var(--space-smallest)) var(--space-small);\n}\n\n._4U2lNr3Kk6xnNZTk3Fxgm {\n grid-template-columns: -webkit-min-content 1fr;\n grid-template-columns: min-content 1fr;\n}\n\n.fl19i7ccgBu7NZkxnpvcI {\n grid-template-columns: 1fr -webkit-min-content;\n grid-template-columns: 1fr min-content;\n}\n\n._4U2lNr3Kk6xnNZTk3Fxgm.fl19i7ccgBu7NZkxnpvcI {\n grid-template-columns: -webkit-min-content 1fr -webkit-min-content;\n grid-template-columns: min-content 1fr min-content;\n}\n\n._3Uf2El5YBbG3DmUWHBhuea,\n._35he2rVDYRU4YQko5r4adP ~ ._2XfthED8eqbKIyTNoH1aWp {\n box-shadow: var(--shadow-base);\n cursor: pointer;\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n\n/**\n * Interaction\n */\n\n._2XfthED8eqbKIyTNoH1aWp:focus,\n._35he2rVDYRU4YQko5r4adP:focus ~ ._2XfthED8eqbKIyTNoH1aWp {\n outline: none;\n}\n\n._3Uf2El5YBbG3DmUWHBhuea:focus,\n._35he2rVDYRU4YQko5r4adP:focus ~ ._2XfthED8eqbKIyTNoH1aWp {\n box-shadow: var(--shadow-focus);\n}\n\n._3Uf2El5YBbG3DmUWHBhuea:hover,\n._3Uf2El5YBbG3DmUWHBhuea:focus,\n._35he2rVDYRU4YQko5r4adP:focus ~ ._2XfthED8eqbKIyTNoH1aWp:not(._18wCcoIrboygQTnIVm8yzS) {\n background-color: var(--color-surface--hover);\n}\n\n/**\n * States\n */\n\n._2XfthED8eqbKIyTNoH1aWp._18wCcoIrboygQTnIVm8yzS {\n border-color: var(--color-surface--reverse);\n color: var(--color-text--reverse);\n background-color: var(--color-surface--reverse);\n}\n\n._2XfthED8eqbKIyTNoH1aWp._1FTJ2ALrm1mVaT7HdMFFGN {\n border-color: var(--color-critical--onSurface);\n color: var(--color-critical--onSurface);\n background-color: var(--color-critical--surface);\n}\n\n._2XfthED8eqbKIyTNoH1aWp.B1ELFzeQy9Ihy9oYcv-px {\n border-color: var(--color-disabled--secondary);\n color: var(--color-disabled);\n background-color: var(--color-disabled--secondary);\n}\n\n._18wCcoIrboygQTnIVm8yzS.B1ELFzeQy9Ihy9oYcv-px {\n border-color: var(--color-disabled);\n color: var(--color-white);\n background-color: var(--color-disabled);\n}\n\n._1FTJ2ALrm1mVaT7HdMFFGN.B1ELFzeQy9Ihy9oYcv-px {\n border-color: var(--color-critical);\n}\n\n/**\n * Input checkbox/radio\n *\n * Hide checkbox on UI but not screen readers and still allow focus state\n */\n\n._35he2rVDYRU4YQko5r4adP {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip: rect(0 0 0 0);\n -webkit-clip-path: inset(100%);\n clip-path: inset(100%);\n white-space: nowrap;\n}\n\n/**\n * Button\n */\n\n._1M_ek3kiaa7OApx5rwBEc3 {\n display: -ms-flexbox;\n display: flex;\n border-radius: var(--radius-base);\n cursor: pointer;\n transition: background-color 200ms ease;\n}\n\n._1M_ek3kiaa7OApx5rwBEc3:hover,\n._1M_ek3kiaa7OApx5rwBEc3:focus {\n background-color: var(--color-surface--hover);\n}\n\n._1M_ek3kiaa7OApx5rwBEc3:focus,\n._3Uf2El5YBbG3DmUWHBhuea ._1M_ek3kiaa7OApx5rwBEc3:hover {\n box-shadow: var(--shadow-focus);\n outline: none;\n}\n\n/**\n * Override icon default colors.\n *\n * NOTE: This is a workaround since some icons have default colors that we don't\n * want to override until the component color prop overrides it.\n * DO NOT COPY!\n */\n\n._3AG5dSWeyKYD1Xfu9fN_DL > svg {\n fill: var(--color-interactive--subtle);\n}\n\n._3AG5dSWeyKYD1Xfu9fN_DL {\n display: -ms-flexbox;\n display: flex;\n}\n";
29
+ var css = "/**\n * Used in the wrapping chips component:\n * - InternalChipSingleSelect\n * - InternalChipMultiSelect\n */\n\n.uhcyg-a-GLRKF_7oHwvMP {\n display: -ms-flexbox;\n display: flex;\n grid-gap: var(--space-small);\n grid-gap: var(--space-small);\n gap: var(--space-small);\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n -ms-flex-align: center;\n align-items: center;\n}\n\n._2XfthED8eqbKIyTNoH1aWp {\n display: inline-grid;\n position: relative;\n max-width: 100%;\n box-sizing: border-box;\n padding: var(--space-small);\n border: var(--border-base) solid var(--color-border);\n border-radius: var(--radius-larger);\n color: var(--color-text--secondary);\n text-align: left;\n vertical-align: middle;\n background-color: var(--color-surface);\n transition: var(--timing-base) ease-in-out;\n transition-property: background-color, box-shadow, color;\n grid-template-columns: -webkit-max-content;\n grid-template-columns: max-content;\n -ms-flex-align: center;\n align-items: center;\n grid-gap: var(--space-small);\n grid-gap: var(--space-small);\n gap: var(--space-small);\n}\n\n._4U2lNr3Kk6xnNZTk3Fxgm,\n.fl19i7ccgBu7NZkxnpvcI {\n padding: calc(var(--space-smaller) + var(--space-smallest)) var(--space-small);\n}\n\n._4U2lNr3Kk6xnNZTk3Fxgm {\n grid-template-columns: -webkit-min-content 1fr;\n grid-template-columns: min-content 1fr;\n}\n\n.fl19i7ccgBu7NZkxnpvcI {\n grid-template-columns: 1fr -webkit-min-content;\n grid-template-columns: 1fr min-content;\n}\n\n._4U2lNr3Kk6xnNZTk3Fxgm.fl19i7ccgBu7NZkxnpvcI {\n grid-template-columns: -webkit-min-content 1fr -webkit-min-content;\n grid-template-columns: min-content 1fr min-content;\n}\n\n._3Uf2El5YBbG3DmUWHBhuea,\n._35he2rVDYRU4YQko5r4adP ~ ._2XfthED8eqbKIyTNoH1aWp {\n box-shadow: var(--shadow-base);\n cursor: pointer;\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n\n/**\n * Interaction\n */\n\n._3Uf2El5YBbG3DmUWHBhuea:hover,\n._2XfthED8eqbKIyTNoH1aWp:focus {\n outline: none;\n background-color: var(--color-surface--hover);\n}\n\n._2XfthED8eqbKIyTNoH1aWp:focus,\n._35he2rVDYRU4YQko5r4adP:focus ~ ._2XfthED8eqbKIyTNoH1aWp {\n box-shadow: var(--shadow-focus);\n}\n\n._35he2rVDYRU4YQko5r4adP:focus ~ ._2XfthED8eqbKIyTNoH1aWp:not(._18wCcoIrboygQTnIVm8yzS) {\n outline: none;\n background-color: var(--color-surface--hover);\n}\n\n/**\n * States\n */\n\n._2XfthED8eqbKIyTNoH1aWp._18wCcoIrboygQTnIVm8yzS {\n border-color: var(--color-surface--reverse);\n color: var(--color-text--reverse);\n background-color: var(--color-surface--reverse);\n}\n\n._2XfthED8eqbKIyTNoH1aWp._1FTJ2ALrm1mVaT7HdMFFGN {\n border-color: var(--color-critical--onSurface);\n color: var(--color-critical--onSurface);\n background-color: var(--color-critical--surface);\n}\n\n._2XfthED8eqbKIyTNoH1aWp.B1ELFzeQy9Ihy9oYcv-px {\n border-color: var(--color-disabled--secondary);\n color: var(--color-disabled);\n background-color: var(--color-disabled--secondary);\n}\n\n._18wCcoIrboygQTnIVm8yzS.B1ELFzeQy9Ihy9oYcv-px {\n border-color: var(--color-disabled);\n color: var(--color-white);\n background-color: var(--color-disabled);\n}\n\n._1FTJ2ALrm1mVaT7HdMFFGN.B1ELFzeQy9Ihy9oYcv-px {\n border-color: var(--color-critical);\n}\n\n/**\n * Input checkbox/radio\n *\n * Hide checkbox on UI but not screen readers and still allow focus state\n */\n\n._35he2rVDYRU4YQko5r4adP {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip: rect(0 0 0 0);\n -webkit-clip-path: inset(100%);\n clip-path: inset(100%);\n white-space: nowrap;\n}\n\n/**\n * Button\n */\n\n._1M_ek3kiaa7OApx5rwBEc3 {\n display: -ms-flexbox;\n display: flex;\n border-radius: var(--radius-base);\n cursor: pointer;\n transition: background-color 200ms ease;\n}\n\n._1M_ek3kiaa7OApx5rwBEc3:hover,\n._1M_ek3kiaa7OApx5rwBEc3:focus {\n background-color: var(--color-surface--hover);\n}\n\n._1M_ek3kiaa7OApx5rwBEc3:focus,\n._3Uf2El5YBbG3DmUWHBhuea ._1M_ek3kiaa7OApx5rwBEc3:hover {\n box-shadow: var(--shadow-focus);\n outline: none;\n}\n\n/**\n * Override icon default colors.\n *\n * NOTE: This is a workaround since some icons have default colors that we don't\n * want to override until the component color prop overrides it.\n * DO NOT COPY!\n */\n\n._3AG5dSWeyKYD1Xfu9fN_DL > svg {\n fill: var(--color-interactive--subtle);\n}\n\n._3AG5dSWeyKYD1Xfu9fN_DL {\n display: -ms-flexbox;\n display: flex;\n}\n";
30
30
  var styles = {"wrapper":"uhcyg-a-GLRKF_7oHwvMP","chip":"_2XfthED8eqbKIyTNoH1aWp","hasPrefix":"_4U2lNr3Kk6xnNZTk3Fxgm","hasSuffix":"fl19i7ccgBu7NZkxnpvcI","clickable":"_3Uf2El5YBbG3DmUWHBhuea","input":"_35he2rVDYRU4YQko5r4adP","active":"_18wCcoIrboygQTnIVm8yzS","invalid":"_1FTJ2ALrm1mVaT7HdMFFGN","disabled":"B1ELFzeQy9Ihy9oYcv-px","button":"_1M_ek3kiaa7OApx5rwBEc3","icon":"_3AG5dSWeyKYD1Xfu9fN_DL"};
31
31
  __chunk_2.styleInject(css);
32
32
 
@@ -97,7 +97,7 @@ function InternalChipAffix(_a) {
97
97
 
98
98
  function InternalChip(_a) {
99
99
  var _b;
100
- var label = _a.label, _c = _a.active, active = _c === void 0 ? false : _c, _d = _a.disabled, disabled = _d === void 0 ? false : _d, _e = _a.invalid, invalid = _e === void 0 ? false : _e, prefix = _a.prefix, suffix = _a.suffix, onClick = _a.onClick;
100
+ var label = _a.label, _c = _a.active, active = _c === void 0 ? false : _c, _d = _a.disabled, disabled = _d === void 0 ? false : _d, _e = _a.invalid, invalid = _e === void 0 ? false : _e, prefix = _a.prefix, suffix = _a.suffix, tabIndex = _a.tabIndex, ariaLabel = _a.ariaLabel, onClick = _a.onClick, onKeyDown = _a.onKeyDown;
101
101
  var _f = __chunk_1.__read(React.useState(), 2), truncateRef = _f[0], setTruncateRef = _f[1];
102
102
  var isClickable = onClick && !disabled;
103
103
  var classNames = classnames(styles.chip, (_b = {},
@@ -110,10 +110,7 @@ function InternalChip(_a) {
110
110
  _b));
111
111
  var affixProps = { active: active, invalid: invalid, disabled: disabled };
112
112
  var Tag = onClick ? "button" : "div";
113
- var chip = (React__default.createElement(Tag, __chunk_1.__assign({ className: classNames }, (isClickable && {
114
- onClick: onClick,
115
- disabled: disabled,
116
- }), { "data-testid": "chip-wrapper" }),
113
+ var chip = (React__default.createElement(Tag, __chunk_1.__assign({ className: classNames }, (isClickable && { onClick: onClick, disabled: disabled }), { onKeyDown: onKeyDown, "data-testid": "chip-wrapper", "aria-label": ariaLabel, tabIndex: tabIndex, role: tabIndex !== undefined ? "option" : undefined }),
117
114
  React__default.createElement(InternalChipAffix, __chunk_1.__assign({ affix: prefix }, affixProps)),
118
115
  React__default.createElement(index.Typography, { element: "span", numberOfLines: 1, size: "base" },
119
116
  React__default.createElement("span", { ref: setTruncateRef }, label)),
@@ -226,10 +223,10 @@ function useInternalChipDismissible(_a) {
226
223
  var availableChipOptions = chipOptions.filter(function (chip) { return !selected.includes(chip.value); });
227
224
  var liveAnnounce = hooks.useLiveAnnounce().liveAnnounce;
228
225
  var actions = {
229
- handleChipRemove: function (chip) {
226
+ handleChipRemove: function (value) {
230
227
  return function () {
231
- liveAnnounce(chip.label + " Removed");
232
- onChange(selected.filter(function (val) { return val !== chip.value; }));
228
+ liveAnnounce(value + " Removed");
229
+ onChange(selected.filter(function (val) { return val !== value; }));
233
230
  };
234
231
  },
235
232
  handleChipAdd: function (value) { return onChange(__chunk_1.__spread(selected, [value])); },
@@ -239,8 +236,39 @@ function useInternalChipDismissible(_a) {
239
236
  return;
240
237
  return function (event) { return onClick(event, value); };
241
238
  },
242
- handleEmptyBackspace: function () {
243
- actions.handleChipRemove(sortedVisibleChipOptions[selected.length - 1])();
239
+ handleWrapperKeyDown: function (event) {
240
+ var target = event.target;
241
+ var isInputAndHasValue = target instanceof HTMLInputElement && target.value;
242
+ if (isInputAndHasValue)
243
+ return;
244
+ var nextElementToFocus = target.nextElementSibling;
245
+ var prevElementToFocus = target.previousElementSibling;
246
+ if (event.key === "ArrowLeft" &&
247
+ prevElementToFocus instanceof HTMLElement) {
248
+ prevElementToFocus.focus();
249
+ }
250
+ if (event.key === "ArrowRight" &&
251
+ nextElementToFocus instanceof HTMLElement) {
252
+ nextElementToFocus.focus();
253
+ }
254
+ },
255
+ handleChipKeyDown: function (value) {
256
+ return function (event) {
257
+ if (event.key === "Backspace" || event.key === "Delete") {
258
+ var target = event.target;
259
+ if (target instanceof HTMLElement) {
260
+ var prevElement = target.previousElementSibling;
261
+ var nextElement = target.nextElementSibling;
262
+ if (prevElement instanceof HTMLElement) {
263
+ prevElement.focus();
264
+ }
265
+ else if (nextElement instanceof HTMLElement) {
266
+ nextElement.focus();
267
+ }
268
+ }
269
+ actions.handleChipRemove(value)();
270
+ }
271
+ };
244
272
  },
245
273
  };
246
274
  return __chunk_1.__assign(__chunk_1.__assign({}, actions), { ref: ref,
@@ -250,7 +278,7 @@ function useInternalChipDismissible(_a) {
250
278
 
251
279
  var menuId = uuid.v1();
252
280
  function useInternalChipDismissibleInput(_a) {
253
- var options = _a.options, _b = _a.isLoadingMore, isLoadingMore = _b === void 0 ? false : _b, onEmptyBackspace = _a.onEmptyBackspace, onCustomOptionSelect = _a.onCustomOptionSelect, onOptionSelect = _a.onOptionSelect, onSearch = _a.onSearch;
281
+ var options = _a.options, _b = _a.isLoadingMore, isLoadingMore = _b === void 0 ? false : _b, onCustomOptionSelect = _a.onCustomOptionSelect, onOptionSelect = _a.onOptionSelect, onSearch = _a.onSearch;
254
282
  var _c = __chunk_1.__read(React.useState([]), 2), allOptions = _c[0], setAllOptions = _c[1];
255
283
  var _d = __chunk_1.__read(React.useState(""), 2), searchValue = _d[0], setSearchValue = _d[1];
256
284
  var _e = __chunk_1.__read(React.useState(false), 2), menuOpen = _e[0], setMenuOpen = _e[1];
@@ -325,7 +353,15 @@ function useInternalChipDismissibleInput(_a) {
325
353
  ArrowUp: function () { return setActiveIndex(computed.previousOptionIndex); },
326
354
  };
327
355
  if (searchValue.length === 0) {
328
- callbacks.Backspace = function () { return onEmptyBackspace(); };
356
+ callbacks.Backspace = function () {
357
+ var _a;
358
+ // If there's no text left to delete,
359
+ // and delete is pressed again, focus on a chip instead.
360
+ var target = (_a = computed.inputRef.current) === null || _a === void 0 ? void 0 : _a.previousElementSibling;
361
+ if (target instanceof HTMLElement) {
362
+ target.focus();
363
+ }
364
+ };
329
365
  }
330
366
  handleKeydownEvents(callbacks, event);
331
367
  },
@@ -446,7 +482,7 @@ function InternalChipDismissibleInput(props) {
446
482
  return (React__default.createElement(React__default.Fragment, null,
447
483
  React__default.createElement("input", { ref: inputRef, className: styles$1.input, type: "text", role: "combobox", "aria-label": "Press up and down arrow to cycle through the options or type to narrow down the results", "aria-autocomplete": "list", "aria-owns": menuId, "aria-expanded": hasAvailableOptions, "aria-activedescendant": generateDescendantId(activeIndex), value: searchValue, onChange: handleSearchChange, onKeyDown: handleKeyDown, onBlur: lodash.debounce(handleBlur, 200), onFocus: handleOpenMenu, autoFocus: true }),
448
484
  (hasAvailableOptions || isLoadingMore) && (React__default.createElement("div", __chunk_1.__assign({ ref: setPositionedElementRef, className: styles$1.menu, style: popperStyles.popper }, attributes.popper),
449
- React__default.createElement("div", { ref: menuRef, role: "listbox", id: menuId, className: styles$1.menuList },
485
+ React__default.createElement("div", { ref: menuRef, role: "listbox", id: menuId, className: styles$1.menuList, "data-testid": "chip-menu" },
450
486
  allOptions.map(function (option, i) {
451
487
  var _a;
452
488
  return (React__default.createElement("button", { key: option.value, role: "option", id: generateDescendantId(i), className: classnames(styles$1.menuListOption, (_a = {},
@@ -461,10 +497,10 @@ function InternalChipDismissibleInput(props) {
461
497
  }
462
498
 
463
499
  function InternalChipDismissible(props) {
464
- var _a = useInternalChipDismissible(props), availableChipOptions = _a.availableChipOptions, wrapperRef = _a.ref, sortedVisibleChipOptions = _a.sortedVisibleChipOptions, handleChipClick = _a.handleChipClick, handleChipAdd = _a.handleChipAdd, handleChipRemove = _a.handleChipRemove, handleCustomAdd = _a.handleCustomAdd, handleEmptyBackspace = _a.handleEmptyBackspace;
465
- return (React__default.createElement("div", { ref: wrapperRef, className: styles$1.wrapper, "data-testid": "dismissible-chips" },
466
- sortedVisibleChipOptions.map(function (chip) { return (React__default.createElement(ChipDismissible, __chunk_1.__assign({ key: chip.value }, chip, { onClick: handleChipClick(chip.value), onRequestRemove: handleChipRemove(chip) }))); }),
467
- React__default.createElement(InternalChipDismissibleInput, { activator: props.activator, attachTo: wrapperRef, isLoadingMore: props.isLoadingMore, options: availableChipOptions, onOptionSelect: handleChipAdd, onCustomOptionSelect: handleCustomAdd, onEmptyBackspace: handleEmptyBackspace, onSearch: props.onSearch, onLoadMore: props.onLoadMore })));
500
+ var _a = useInternalChipDismissible(props), availableChipOptions = _a.availableChipOptions, wrapperRef = _a.ref, sortedVisibleChipOptions = _a.sortedVisibleChipOptions, handleChipAdd = _a.handleChipAdd, handleChipClick = _a.handleChipClick, handleChipKeyDown = _a.handleChipKeyDown, handleChipRemove = _a.handleChipRemove, handleCustomAdd = _a.handleCustomAdd, handleWrapperKeyDown = _a.handleWrapperKeyDown;
501
+ return (React__default.createElement("div", { ref: wrapperRef, className: styles$1.wrapper, "data-testid": "dismissible-chips", onKeyDown: handleWrapperKeyDown, role: "listbox" },
502
+ sortedVisibleChipOptions.map(function (chip) { return (React__default.createElement(InternalChip, __chunk_1.__assign({ key: chip.value }, chip, { onKeyDown: handleChipKeyDown(chip.value), onClick: handleChipClick(chip.value), ariaLabel: chip.value + ". Press delete or backspace to remove " + chip.value, tabIndex: 0, suffix: React__default.createElement(InternalChipButton, { icon: "remove", invalid: chip.invalid, disabled: chip.disabled, label: chip.label, onClick: handleChipRemove(chip.value) }) }))); }),
503
+ React__default.createElement(InternalChipDismissibleInput, { activator: props.activator, attachTo: wrapperRef, isLoadingMore: props.isLoadingMore, options: availableChipOptions, onOptionSelect: handleChipAdd, onCustomOptionSelect: handleCustomAdd, onSearch: props.onSearch, onLoadMore: props.onLoadMore })));
468
504
  }
469
505
 
470
506
  function Chips(props) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jobber/components",
3
- "version": "2.79.0",
3
+ "version": "2.80.0",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -85,5 +85,5 @@
85
85
  "> 1%",
86
86
  "IE 10"
87
87
  ],
88
- "gitHead": "d7c9cd86f4cfc7c3d21ef6c6d07773675cad69b7"
88
+ "gitHead": "74a06594d91c1808e47dad89994633817c3c55e0"
89
89
  }