diff --git a/playbook/app/pb_kits/playbook/pb_checkbox/_checkbox.tsx b/playbook/app/pb_kits/playbook/pb_checkbox/_checkbox.tsx index 5f44960cbf..1ee41be533 100644 --- a/playbook/app/pb_kits/playbook/pb_checkbox/_checkbox.tsx +++ b/playbook/app/pb_kits/playbook/pb_checkbox/_checkbox.tsx @@ -8,18 +8,18 @@ import { globalProps, GlobalProps } from '../utilities/globalProps' type CheckboxProps = { aria?: {[key: string]: string}, checked?: boolean, - children: Node, + children?: React.ReactChild[] | React.ReactChild, className?: string, dark?: boolean, data?: {[key: string]: string}, error?: boolean, id?: string, indeterminate?: boolean, - name: string, - onChange: (event: React.FormEvent) => void, - tabIndex: number, - text: string, - value: string, + name?: string, + onChange?: (event: React.FormEvent) => void, + tabIndex?: number, + text?: string, + value?: string, } & GlobalProps const Checkbox = (props: CheckboxProps): JSX.Element => { @@ -34,7 +34,7 @@ const Checkbox = (props: CheckboxProps): JSX.Element => { id, indeterminate = false, name = '', - onChange = () => {}, + onChange = () => { void 0 }, tabIndex, text = '', value = '', diff --git a/playbook/app/pb_kits/playbook/pb_radio/_radio.tsx b/playbook/app/pb_kits/playbook/pb_radio/_radio.tsx index 80ac8cdb70..62d5156e6b 100644 --- a/playbook/app/pb_kits/playbook/pb_radio/_radio.tsx +++ b/playbook/app/pb_kits/playbook/pb_radio/_radio.tsx @@ -10,16 +10,16 @@ type RadioProps = { aria?: {[key: string]: string}, alignment?: string, checked?: boolean, - children?: Node, + children?: React.ReactChild[] | React.ReactChild, className?: string, dark?: boolean, data?: {[key: string]: string}, error?: boolean, id?: string, label: string, - name: string, - value: string, - text: string, + name?: string, + value?: string, + text?: string, onChange: (event: React.FormEvent | null)=>void, } & GlobalProps diff --git a/playbook/app/pb_kits/playbook/pb_selectable_card/_selectable_card.jsx b/playbook/app/pb_kits/playbook/pb_selectable_card/_selectable_card.tsx similarity index 67% rename from playbook/app/pb_kits/playbook/pb_selectable_card/_selectable_card.jsx rename to playbook/app/pb_kits/playbook/pb_selectable_card/_selectable_card.tsx index 1d50720ae7..c80019e4b0 100644 --- a/playbook/app/pb_kits/playbook/pb_selectable_card/_selectable_card.jsx +++ b/playbook/app/pb_kits/playbook/pb_selectable_card/_selectable_card.tsx @@ -1,10 +1,9 @@ /* @flow */ -import React from 'react' +import React, {useRef} from 'react' import classnames from 'classnames' -import type { InputCallback } from '../types' -import { globalProps } from '../utilities/globalProps' +import { globalProps, GlobalProps } from '../utilities/globalProps' import { buildAriaProps, buildCss, @@ -19,13 +18,13 @@ import Flex from '../pb_flex/_flex' import Radio from '../pb_radio/_radio' type SelectableCardProps = { - aria?: object, - checked: boolean, - children?: array, + aria?: { [key: string]: string }, + checked?: boolean, + children?: React.ReactChild[] | React.ReactChild, className?: string, - customIcon?: SVGElement, + customIcon?: {[key: string] :SVGElement}, dark?: boolean, - data: object, + data?: { [key: string]: string }, disabled?: boolean, error?: boolean, icon?: boolean, @@ -33,32 +32,31 @@ type SelectableCardProps = { inputId?: string, multi?: boolean, name?: string, - onChange: InputCallback, + onChange: (event: React.FormEvent) => void, text?: string, value?: string, variant?: string, -} +} & GlobalProps -const SelectableCard = ({ - aria = {}, - checked = false, - children, - className, - customIcon, - dark = false, - data = {}, - disabled = false, - error = false, - icon = false, - inputId = null, - multi = true, - name, - onChange = noop, - text, - value, - variant = 'default', - ...props -}: SelectableCardProps) => { +const SelectableCard = (props: SelectableCardProps) => { + const { + aria = {}, + checked = false, + className, + customIcon, + dark = false, + data = {}, + disabled = false, + error = false, + icon = false, + inputId = null, + multi = true, + name, + onChange = noop, + text, + value, + variant = 'default', + } = props const ariaProps = buildAriaProps(aria) const dataProps = buildDataProps(data) @@ -87,7 +85,7 @@ const SelectableCard = ({ } } - const inputRef = React.createRef() + const inputRef = useRef(null) // Delegate clicks to hidden input from visible one const handleClick = () => { inputRef.current.click() @@ -96,7 +94,15 @@ const SelectableCard = ({ const inputType = multi ? 'checkbox' : 'radio' const inputIdPresent = inputId !== null ? inputId : name const Input = multi ? Checkbox : Radio - const labelProps = variant === 'displayInput' ? Object.assign(props, { padding: 'none' }) : props + + const filteredProps = {...props} + delete filteredProps?.inputId + delete filteredProps?.children + delete filteredProps?.icon + delete filteredProps?.error + delete filteredProps?.dark + delete filteredProps?.multi + const labelProps: GlobalProps = variant === 'displayInput' ? { ...filteredProps, padding: 'none' } : { ...filteredProps } return (
diff --git a/playbook/app/pb_kits/playbook/pb_selectable_card/docs/_selectable_card_default.jsx b/playbook/app/pb_kits/playbook/pb_selectable_card/docs/_selectable_card_default.jsx index 30782f980e..9572074456 100644 --- a/playbook/app/pb_kits/playbook/pb_selectable_card/docs/_selectable_card_default.jsx +++ b/playbook/app/pb_kits/playbook/pb_selectable_card/docs/_selectable_card_default.jsx @@ -1,5 +1,5 @@ import React, { useState } from 'react' -import SelectableCard from '../_selectable_card.jsx' +import SelectableCard from '../_selectable_card.tsx' const SelectableCardDefault = (props) => { const [selectedWithIcon, setSelectedWithIcon] = useState(true) @@ -24,7 +24,6 @@ const SelectableCardDefault = (props) => { setSelectedNoIcon(!selectedNoIcon)} diff --git a/playbook/app/pb_kits/playbook/pb_selectable_card/docs/_selectable_card_single_select.jsx b/playbook/app/pb_kits/playbook/pb_selectable_card/docs/_selectable_card_single_select.jsx index add4e79d1e..00714471fe 100644 --- a/playbook/app/pb_kits/playbook/pb_selectable_card/docs/_selectable_card_single_select.jsx +++ b/playbook/app/pb_kits/playbook/pb_selectable_card/docs/_selectable_card_single_select.jsx @@ -1,5 +1,5 @@ import React, { useState } from 'react' -import SelectableCard from '../_selectable_card.jsx' +import SelectableCard from '../_selectable_card.tsx' const SelectableCardSingleSelect = (props) => { const [selected, setSelected] = useState(null) diff --git a/playbook/app/pb_kits/playbook/pb_selectable_card/selectable_card.test.js b/playbook/app/pb_kits/playbook/pb_selectable_card/selectable_card.test.js new file mode 100644 index 0000000000..87b6cc5b6d --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_selectable_card/selectable_card.test.js @@ -0,0 +1,185 @@ +import React, { useState } from 'react' +import { render, screen } from '../utilities/test-utils' +import SelectableCard from './_selectable_card' +import { Body, Title, Image } from '../' + +const SelectableCardMultiSelect = () => { + const [selected, setSelected] = useState(true) + const [unselected, setUnselected] = useState(false) + const [disabled, setDisabled] = useState(false) + + return ( + <> + setSelected(!selected)} + value="selected" + > + {'Selected'} + + + setUnselected(!unselected)} + value="unselected" + > + {'Unselected'} + + + setDisabled(!disabled)} + value="disabled" + > + {'Disabled'} + + + + ) +} + +const SelectableCardSingleSelect = () => { + const [selected, setSelected] = useState(null) + const handleSelect = (event) => { + setSelected(event.target.value) + } + + return ( + <> + + {'Male'} + + + + {'Female'} + + + + {'Other'} + + + ) +} + + +test('should start with a checked item', () => { + render() + + const kit = screen.getByLabelText('Selected') + expect(kit).toBeChecked() +}) + +test('should start with a disabled item', () => { + render() + + const kit = screen.getByLabelText('Disabled') + expect(kit).toBeDisabled() +}) + +test('should click and check an item', () => { + render() + + const kit = screen.getByLabelText('Unselected') + expect(kit).not.toBeChecked() + kit.click() + expect(kit).toBeChecked() +}) + +test('should check multiple items', () => { + render() + + const checkedItem = screen.getByLabelText('Selected') + expect(checkedItem).toBeChecked() + + const uncheckedItem = screen.getByLabelText('Unselected') + expect(uncheckedItem).not.toBeChecked() + + uncheckedItem.click() + + expect(checkedItem).toBeChecked + expect(uncheckedItem).toBeChecked +}) + +test('should check only single item', () => { + render() + + const male = screen.getByLabelText('Male') + expect(male).not.toBeChecked + + const female = screen.getByLabelText('Female') + expect(female).not.toBeChecked + + const other = screen.getByLabelText('Other') + expect(other).not.toBeChecked + + male.click() + other.click() + + expect(male).not.toBeChecked + expect(female).not.toBeChecked + expect(other).toBeChecked +}) + +test('should have text passed through the text prop', () => { + render () + + expect(screen.getByText("This passes text through the tag")).toBeInTheDocument() +}) + +test('should pass block content', () => { + render ( + + <Body + tag="span" + > + {'This uses block'} + </Body> + </SelectableCard> + ) + expect(screen.getByText("This uses block")).toBeInTheDocument() +}) + +test('should pass image inside card content', () => { + render (<SelectableCard> + <Image + rounded + size="xl" + url="https://unsplash.it/500/400/?image=634" + /> + </SelectableCard> + ) + const dispalyedImg = document.querySelector("img") + + expect(dispalyedImg.src).toContain("image=634") +})