@toptal/picasso-checkbox 100.0.0 → 100.1.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/dist-package/src/Checkbox/Checkbox.d.ts +5 -0
- package/dist-package/src/Checkbox/Checkbox.d.ts.map +1 -1
- package/dist-package/src/Checkbox/Checkbox.js +7 -4
- package/dist-package/src/Checkbox/Checkbox.js.map +1 -1
- package/package.json +6 -6
- package/src/Checkbox/Checkbox.tsx +12 -1
- package/src/Checkbox/test.tsx +34 -0
|
@@ -21,6 +21,11 @@ export interface Props extends BaseProps, TextLabelProps, Omit<ButtonOrAnchorPro
|
|
|
21
21
|
onChange?: (event: React.ChangeEvent<HTMLInputElement>, checked: boolean) => void;
|
|
22
22
|
/** Value of the `Checkbox` (applicable only for controlled component) */
|
|
23
23
|
value?: string;
|
|
24
|
+
/** Testids of the `Checkbox` internal elements. The top-level `data-testid` stays on the `[role="checkbox"]` element */
|
|
25
|
+
testIds?: {
|
|
26
|
+
/** Testid of the visually hidden native `<input>` (rendered as a sibling of the `[role="checkbox"]` element) */
|
|
27
|
+
input?: string;
|
|
28
|
+
};
|
|
24
29
|
}
|
|
25
30
|
export declare const Checkbox: React.ForwardRefExoticComponent<Props & React.RefAttributes<HTMLButtonElement | HTMLLabelElement | HTMLDivElement>>;
|
|
26
31
|
export default Checkbox;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Checkbox.d.ts","sourceRoot":"","sources":["../../../src/Checkbox/Checkbox.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,mBAAmB,EACnB,SAAS,EACT,cAAc,EACf,MAAM,wBAAwB,CAAA;AAE/B,OAAO,KAAK,EAAkB,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACrE,OAAO,KAAqB,MAAM,OAAO,CAAA;AAEzC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AAiBpE,MAAM,WAAW,KACf,SAAQ,SAAS,EACf,cAAc,EACd,IAAI,CAAC,mBAAmB,EAAE,UAAU,CAAC;IACvC,iCAAiC;IACjC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,+DAA+D;IAC/D,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,oCAAoC;IACpC,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,kCAAkC;IAClC,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,oBAAoB;IACpB,UAAU,CAAC,EAAE,aAAa,CAAA;IAC1B,8FAA8F;IAC9F,kBAAkB,CAAC,EAAE,kBAAkB,CAAA;IACvC,yDAAyD;IACzD,QAAQ,CAAC,EAAE,CACT,KAAK,EAAE,KAAK,CAAC,WAAW,CAAC,gBAAgB,CAAC,EAC1C,OAAO,EAAE,OAAO,KACb,IAAI,CAAA;IACT,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"Checkbox.d.ts","sourceRoot":"","sources":["../../../src/Checkbox/Checkbox.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,mBAAmB,EACnB,SAAS,EACT,cAAc,EACf,MAAM,wBAAwB,CAAA;AAE/B,OAAO,KAAK,EAAkB,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACrE,OAAO,KAAqB,MAAM,OAAO,CAAA;AAEzC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AAiBpE,MAAM,WAAW,KACf,SAAQ,SAAS,EACf,cAAc,EACd,IAAI,CAAC,mBAAmB,EAAE,UAAU,CAAC;IACvC,iCAAiC;IACjC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,+DAA+D;IAC/D,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,oCAAoC;IACpC,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,kCAAkC;IAClC,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,oBAAoB;IACpB,UAAU,CAAC,EAAE,aAAa,CAAA;IAC1B,8FAA8F;IAC9F,kBAAkB,CAAC,EAAE,kBAAkB,CAAA;IACvC,yDAAyD;IACzD,QAAQ,CAAC,EAAE,CACT,KAAK,EAAE,KAAK,CAAC,WAAW,CAAC,gBAAgB,CAAC,EAC1C,OAAO,EAAE,OAAO,KACb,IAAI,CAAA;IACT,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,wHAAwH;IACxH,OAAO,CAAC,EAAE;QACR,gHAAgH;QAChH,KAAK,CAAC,EAAE,MAAM,CAAA;KACf,CAAA;CACF;AAED,eAAO,MAAM,QAAQ,qHA+FnB,CAAA;AAIF,eAAe,QAAQ,CAAA"}
|
|
@@ -11,7 +11,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
11
11
|
};
|
|
12
12
|
import { Checkbox as BaseCheckbox } from '@base-ui/react/checkbox';
|
|
13
13
|
import { useBaseUiId } from '@base-ui/react/internals/useBaseUiId';
|
|
14
|
-
import { toReactChangeEvent } from '@toptal/picasso-shared';
|
|
14
|
+
import { toReactChangeEvent, useInputTestId } from '@toptal/picasso-shared';
|
|
15
15
|
import React, { forwardRef } from 'react';
|
|
16
16
|
import { FormControlLabel } from '@toptal/picasso-form-label';
|
|
17
17
|
import { twJoin, twMerge } from '@toptal/picasso-tailwind-merge';
|
|
@@ -19,9 +19,9 @@ import { checkboxClassNames } from './styles';
|
|
|
19
19
|
export const Checkbox = forwardRef(function Checkbox(_a, ref) {
|
|
20
20
|
var _b;
|
|
21
21
|
var { disabled = false, indeterminate = false, onChange = () => { } } = _a, props = __rest(_a, ["disabled", "indeterminate", "onChange"]);
|
|
22
|
-
const { label, id, className, style, labelStyle, requiredDecoration, value, checked, titleCase } = props, rest = __rest(props
|
|
22
|
+
const { label, id, className, style, labelStyle, requiredDecoration, value, checked, titleCase, testIds } = props, rest = __rest(props
|
|
23
23
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
24
|
-
, ["label", "id", "className", "style", "labelStyle", "requiredDecoration", "value", "checked", "titleCase"]);
|
|
24
|
+
, ["label", "id", "className", "style", "labelStyle", "requiredDecoration", "value", "checked", "titleCase", "testIds"]);
|
|
25
25
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
26
26
|
const { color, 'data-private': dataPrivate } = rest, checkboxAttributes = __rest(rest
|
|
27
27
|
// Public Props extend button/anchor HTML attributes, but the rendered control
|
|
@@ -36,13 +36,16 @@ export const Checkbox = forwardRef(function Checkbox(_a, ref) {
|
|
|
36
36
|
// node (see FormControlLabel `labelId`), only when there is a label.
|
|
37
37
|
const generatedLabelId = useBaseUiId();
|
|
38
38
|
const labelId = label ? generatedLabelId : undefined;
|
|
39
|
+
// Base UI renders the hidden input beside the root and exposes it only
|
|
40
|
+
// through `inputRef`, so the testid is stamped on it imperatively
|
|
41
|
+
const inputTestIdRef = useInputTestId(testIds === null || testIds === void 0 ? void 0 : testIds.input);
|
|
39
42
|
const checkboxElement = (
|
|
40
43
|
// The visually-hidden <input> ships inline `position:absolute` with a
|
|
41
44
|
// negative margin; `relative` + `translate-px` anchor it inside the 16px
|
|
42
45
|
// box so it doesn't grow the rendered box, and `appearance-none` stops the
|
|
43
46
|
// native control painting over it.
|
|
44
47
|
React.createElement("span", { className: 'relative inline-flex self-start align-middle [&_input]:appearance-none [&_input]:translate-x-px [&_input]:translate-y-px' },
|
|
45
|
-
React.createElement(BaseCheckbox.Root, Object.assign({}, rootRest, { ref: label ? undefined : ref, "aria-labelledby": (_b = rootRest['aria-labelledby']) !== null && _b !== void 0 ? _b : labelId, checked: checked, disabled: disabled, id: id, value: value, indeterminate: indeterminate, onCheckedChange: (nextChecked, { event }) => onChange(toReactChangeEvent(event), nextChecked), className: twMerge(checkboxClassNames, className), style: style }))));
|
|
48
|
+
React.createElement(BaseCheckbox.Root, Object.assign({}, rootRest, { ref: label ? undefined : ref, inputRef: inputTestIdRef, "aria-labelledby": (_b = rootRest['aria-labelledby']) !== null && _b !== void 0 ? _b : labelId, checked: checked, disabled: disabled, id: id, value: value, indeterminate: indeterminate, onCheckedChange: (nextChecked, { event }) => onChange(toReactChangeEvent(event), nextChecked), className: twMerge(checkboxClassNames, className), style: style }))));
|
|
46
49
|
if (!label) {
|
|
47
50
|
return checkboxElement;
|
|
48
51
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Checkbox.js","sourceRoot":"","sources":["../../../src/Checkbox/Checkbox.tsx"],"names":[],"mappings":";;;;;;;;;;;AAAA,OAAO,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAA;AAMlE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"Checkbox.js","sourceRoot":"","sources":["../../../src/Checkbox/Checkbox.tsx"],"names":[],"mappings":";;;;;;;;;;;AAAA,OAAO,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAA;AAMlE,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAE3E,OAAO,KAAK,EAAE,EAAE,UAAU,EAAE,MAAM,OAAO,CAAA;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAE7D,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAA;AAEhE,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AA8C7C,MAAM,CAAC,MAAM,QAAQ,GAAG,UAAU,CAGhC,SAAS,QAAQ,CACjB,EAA0E,EAC1E,GAAG;;QADH,EAAE,QAAQ,GAAG,KAAK,EAAE,aAAa,GAAG,KAAK,EAAE,QAAQ,GAAG,GAAG,EAAE,GAAE,CAAC,OAAY,EAAP,KAAK,cAAxE,yCAA0E,CAAF;IAGxE,MAAM,EACJ,KAAK,EACL,EAAE,EACF,SAAS,EACT,KAAK,EACL,UAAU,EACV,kBAAkB,EAClB,KAAK,EACL,OAAO,EACP,SAAS,EACT,OAAO,KAEL,KAAK,EADJ,IAAI,UACL,KAAK;IAET,6DAA6D;MAdvD,qHAYL,CAAQ,CAAA;IAET,6DAA6D;IAC7D,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,WAAW,KAA4B,IAAI,EAA3B,kBAAkB,UAAK,IAAI;IAE1E,8EAA8E;IAC9E,0EAA0E;IAC1E,uEAAuE;MAJjE,yBAA6D,CAAO,CAAA;IAE1E,8EAA8E;IAC9E,0EAA0E;IAC1E,uEAAuE;IACvE,MAAM,QAAQ,GAAG,kBAAuC,CAAA;IAExD,8EAA8E;IAC9E,qEAAqE;IACrE,MAAM,gBAAgB,GAAG,WAAW,EAAE,CAAA;IACtC,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS,CAAA;IAEpD,uEAAuE;IACvE,kEAAkE;IAClE,MAAM,cAAc,GAAG,cAAc,CAAC,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,KAAK,CAAC,CAAA;IAErD,MAAM,eAAe,GAAG;IACtB,sEAAsE;IACtE,yEAAyE;IACzE,2EAA2E;IAC3E,mCAAmC;IACnC,8BAAM,SAAS,EAAC,0HAA0H;QACxI,oBAAC,YAAY,CAAC,IAAI,oBACZ,QAAQ,IACZ,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAE,GAA8B,EACxD,QAAQ,EAAE,cAAc,qBACP,MAAA,QAAQ,CAAC,iBAAiB,CAAC,mCAAI,OAAO,EACvD,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,QAAQ,EAClB,EAAE,EAAE,EAAE,EACN,KAAK,EAAE,KAAK,EACZ,aAAa,EAAE,aAAa,EAC5B,eAAe,EAAE,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAC1C,QAAQ,CAAC,kBAAkB,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,EAElD,SAAS,EAAE,OAAO,CAAC,kBAAkB,EAAE,SAAS,CAAC,EACjD,KAAK,EAAE,KAAK,IACZ,CACG,CACR,CAAA;IAED,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,eAAe,CAAA;IACxB,CAAC;IAED,MAAM,sBAAsB,GAAG;QAC7B,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,WAAW,EAAE,IAAI,CAAC,WAAW;KACa,CAAA;IAE5C,OAAO,CACL,oBAAC,gBAAgB,oBACX,sBAAsB,IAC1B,KAAK,EAAE,UAAU,EACjB,GAAG,EAAE,GAA4D,EACjE,OAAO,EAAE;YACP,IAAI,EAAE,aAAa;YACnB,KAAK,EAAE,MAAM,CACX,kCAAkC,EAClC,YAAY,EACZ,QAAQ,IAAI,eAAe,CAC5B;SACF,EACD,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,OAAO,EAChB,kBAAkB,EAAE,kBAAkB,EACtC,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,SAAS,EACpB,SAAS,EAAC,kBAAkB,kBACd,WAAW,IACzB,CACH,CAAA;AACH,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,WAAW,GAAG,UAAU,CAAA;AAEjC,eAAe,QAAQ,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toptal/picasso-checkbox",
|
|
3
|
-
"version": "100.
|
|
3
|
+
"version": "100.1.0",
|
|
4
4
|
"description": "Toptal UI components library - Checkbox",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
"@base-ui/react": "^1.6.0",
|
|
22
22
|
"ap-style-title-case": "^1.1.2",
|
|
23
23
|
"classnames": "^2.5.1",
|
|
24
|
-
"@toptal/picasso-
|
|
25
|
-
"@toptal/picasso-
|
|
26
|
-
"@toptal/picasso-shared": "100.
|
|
27
|
-
"@toptal/picasso-utils": "100.0.
|
|
24
|
+
"@toptal/picasso-form-label": "100.0.1",
|
|
25
|
+
"@toptal/picasso-grid": "100.0.1",
|
|
26
|
+
"@toptal/picasso-shared": "100.1.0",
|
|
27
|
+
"@toptal/picasso-utils": "100.0.1"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
30
|
"react": ">=17.0.0 < 19.0.0",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"styled-components": "^6.1.1",
|
|
36
36
|
"@toptal/picasso-provider": "100.0.0",
|
|
37
37
|
"@toptal/picasso-tailwind-merge": "100.0.0",
|
|
38
|
-
"@toptal/picasso-test-utils": "100.0.
|
|
38
|
+
"@toptal/picasso-test-utils": "100.0.1"
|
|
39
39
|
},
|
|
40
40
|
"exports": {
|
|
41
41
|
".": "./dist-package/src/index.js"
|
|
@@ -5,7 +5,7 @@ import type {
|
|
|
5
5
|
BaseProps,
|
|
6
6
|
TextLabelProps,
|
|
7
7
|
} from '@toptal/picasso-shared'
|
|
8
|
-
import { toReactChangeEvent } from '@toptal/picasso-shared'
|
|
8
|
+
import { toReactChangeEvent, useInputTestId } from '@toptal/picasso-shared'
|
|
9
9
|
import type { ComponentProps, CSSProperties, ReactNode } from 'react'
|
|
10
10
|
import React, { forwardRef } from 'react'
|
|
11
11
|
import { FormControlLabel } from '@toptal/picasso-form-label'
|
|
@@ -51,6 +51,11 @@ export interface Props
|
|
|
51
51
|
) => void
|
|
52
52
|
/** Value of the `Checkbox` (applicable only for controlled component) */
|
|
53
53
|
value?: string
|
|
54
|
+
/** Testids of the `Checkbox` internal elements. The top-level `data-testid` stays on the `[role="checkbox"]` element */
|
|
55
|
+
testIds?: {
|
|
56
|
+
/** Testid of the visually hidden native `<input>` (rendered as a sibling of the `[role="checkbox"]` element) */
|
|
57
|
+
input?: string
|
|
58
|
+
}
|
|
54
59
|
}
|
|
55
60
|
|
|
56
61
|
export const Checkbox = forwardRef<
|
|
@@ -70,6 +75,7 @@ export const Checkbox = forwardRef<
|
|
|
70
75
|
value,
|
|
71
76
|
checked,
|
|
72
77
|
titleCase,
|
|
78
|
+
testIds,
|
|
73
79
|
...rest
|
|
74
80
|
} = props
|
|
75
81
|
|
|
@@ -86,6 +92,10 @@ export const Checkbox = forwardRef<
|
|
|
86
92
|
const generatedLabelId = useBaseUiId()
|
|
87
93
|
const labelId = label ? generatedLabelId : undefined
|
|
88
94
|
|
|
95
|
+
// Base UI renders the hidden input beside the root and exposes it only
|
|
96
|
+
// through `inputRef`, so the testid is stamped on it imperatively
|
|
97
|
+
const inputTestIdRef = useInputTestId(testIds?.input)
|
|
98
|
+
|
|
89
99
|
const checkboxElement = (
|
|
90
100
|
// The visually-hidden <input> ships inline `position:absolute` with a
|
|
91
101
|
// negative margin; `relative` + `translate-px` anchor it inside the 16px
|
|
@@ -95,6 +105,7 @@ export const Checkbox = forwardRef<
|
|
|
95
105
|
<BaseCheckbox.Root
|
|
96
106
|
{...rootRest}
|
|
97
107
|
ref={label ? undefined : (ref as React.Ref<HTMLElement>)}
|
|
108
|
+
inputRef={inputTestIdRef}
|
|
98
109
|
aria-labelledby={rootRest['aria-labelledby'] ?? labelId}
|
|
99
110
|
checked={checked}
|
|
100
111
|
disabled={disabled}
|
package/src/Checkbox/test.tsx
CHANGED
|
@@ -220,4 +220,38 @@ describe('Checkbox', () => {
|
|
|
220
220
|
})
|
|
221
221
|
})
|
|
222
222
|
})
|
|
223
|
+
|
|
224
|
+
describe('testIds', () => {
|
|
225
|
+
it('keeps the top-level data-testid on the role element', () => {
|
|
226
|
+
const { getByTestId, getByRole } = render(
|
|
227
|
+
<Checkbox data-testid='checkbox' label='Select item' />
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
expect(getByTestId('checkbox')).toBe(getByRole('checkbox'))
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
it('places testIds.input on the visually hidden native input', () => {
|
|
234
|
+
const { getByTestId, getByRole } = render(
|
|
235
|
+
<Checkbox label='Select item' testIds={{ input: 'checkbox-input' }} />
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
const input = getByTestId('checkbox-input')
|
|
239
|
+
|
|
240
|
+
expect(input).toBeInstanceOf(HTMLInputElement)
|
|
241
|
+
// the input is rendered beside the role element, not inside it
|
|
242
|
+
expect(getByRole('checkbox')).not.toContainElement(input)
|
|
243
|
+
|
|
244
|
+
fireEvent.click(getByRole('checkbox'))
|
|
245
|
+
|
|
246
|
+
expect(input).toBeChecked()
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
it('places testIds.input on the input when there is no label', () => {
|
|
250
|
+
const { getByTestId } = render(
|
|
251
|
+
<Checkbox testIds={{ input: 'checkbox-input' }} />
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
expect(getByTestId('checkbox-input')).toBeInstanceOf(HTMLInputElement)
|
|
255
|
+
})
|
|
256
|
+
})
|
|
223
257
|
})
|