diff --git a/packages/react/src/components/DataTable/TableSelectRow.tsx b/packages/react/src/components/DataTable/TableSelectRow.tsx index bfe38b550e37..8a209dacf62e 100644 --- a/packages/react/src/components/DataTable/TableSelectRow.tsx +++ b/packages/react/src/components/DataTable/TableSelectRow.tsx @@ -87,29 +87,57 @@ const TableSelectRow = ({ }: TableSelectRowProps) => { const prefix = usePrefix(); const uniqueNameId = useId(); + + const handleRadioChange = onChange + ? ( + value: string | number | undefined, + name: string | undefined, + event: React.ChangeEvent + ) => { + // Convert the radio value to boolean for consistency + onChange(!!value, name || '', event); + } + : undefined; + + const handleCheckboxChange = onChange + ? ( + checked: boolean, + name: string, + event: React.ChangeEvent + ) => { + onChange(checked, name, event); + } + : undefined; + const selectionInputProps = { id, name: name ? name : uniqueNameId, onClick: onSelect, - onChange, checked, disabled, }; - const InlineInputComponent = radio ? RadioButton : InlineCheckbox; + + const labelValue = ariaLabel || deprecatedAriaLabel || ''; const tableSelectRowClasses = classNames(`${prefix}--table-column-checkbox`, { ...(className && { [className]: true }), [`${prefix}--table-column-radio`]: radio, }); return ( - + {radio ? ( + + ) : ( + + )} ); }; diff --git a/packages/react/src/components/DatePicker/DatePicker-test.js b/packages/react/src/components/DatePicker/DatePicker-test.js index d1c9341e0d9e..2a99afeb8b81 100644 --- a/packages/react/src/components/DatePicker/DatePicker-test.js +++ b/packages/react/src/components/DatePicker/DatePicker-test.js @@ -712,4 +712,45 @@ describe('Date picker with minDate and maxDate', () => { await userEvent.keyboard('{escape}'); expect(screen.getByRole('application')).not.toHaveClass('open'); }); + it('clearing end date should not cause console warnings', async () => { + const warn = jest.spyOn(console, 'warn').mockImplementation(() => {}); + + render( + {}} datePickerType="range" dateFormat="m/d/Y"> + + + + ); + await userEvent.type( + screen.getByLabelText('Start Date'), + '01/01/2024{enter}' + ); + await userEvent.type( + screen.getByLabelText('End Date'), + '01/15/2024{enter}' + ); + + // Ensure the dates are correctly populated + expect(screen.getByLabelText('Start Date')).toHaveValue('01/01/2024'); + expect(screen.getByLabelText('End Date')).toHaveValue('01/15/2024'); + + // Clear the end date + await userEvent.clear(screen.getByLabelText('End Date')); + expect(screen.getByLabelText('End Date')).toHaveValue(''); + + // Click on the start date input after clearing the end date + await userEvent.click(screen.getByLabelText('Start Date')); + expect(warn).not.toHaveBeenCalled(); + warn.mockRestore(); + }); }); diff --git a/packages/react/src/components/DatePicker/plugins/fixEventsPlugin.js b/packages/react/src/components/DatePicker/plugins/fixEventsPlugin.js index e779049b72a5..831dbd63892e 100644 --- a/packages/react/src/components/DatePicker/plugins/fixEventsPlugin.js +++ b/packages/react/src/components/DatePicker/plugins/fixEventsPlugin.js @@ -81,7 +81,7 @@ export default (config) => (fp) => { if (inputTo === target && fp.selectedDates[1]) { // Using getTime() enables the ability to more readily compare the date currently // selected in the calendar and the date currently in the value of the input - const withoutTime = (date) => date.setHours(0, 0, 0, 0); + const withoutTime = (date) => date?.setHours(0, 0, 0, 0); const selectedToDate = withoutTime(new Date(fp.selectedDates[1])); const currentValueToDate = withoutTime( parseDateWithFormat(inputTo.value) @@ -104,7 +104,7 @@ export default (config) => (fp) => { } } - const isValidDate = (date) => date.toString() !== 'Invalid Date'; + const isValidDate = (date) => date?.toString() !== 'Invalid Date'; // save end date in calendar inmediately after it's been written down if (inputTo === target && fp.selectedDates.length === 1 && inputTo.value) { if (isValidDate(parseDateWithFormat(inputTo.value))) { diff --git a/packages/react/src/components/InlineCheckbox/InlineCheckbox.js b/packages/react/src/components/InlineCheckbox/InlineCheckbox.tsx similarity index 63% rename from packages/react/src/components/InlineCheckbox/InlineCheckbox.js rename to packages/react/src/components/InlineCheckbox/InlineCheckbox.tsx index ae1b45d26325..1c8c455412ae 100644 --- a/packages/react/src/components/InlineCheckbox/InlineCheckbox.js +++ b/packages/react/src/components/InlineCheckbox/InlineCheckbox.tsx @@ -4,15 +4,78 @@ * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ - import PropTypes from 'prop-types'; import React, { useEffect, useRef } from 'react'; import deprecate from '../../prop-types/deprecate'; import { usePrefix } from '../../internal/usePrefix'; import { useMergedRefs } from '../../internal/useMergedRefs'; -/** @type any */ -const InlineCheckbox = React.forwardRef( +export interface InlineCheckboxProps { + /* + * Specify the label for the control + */ + 'aria-label': string; + + /** + * Deprecated, please use `aria-label` instead. + * Specify the label for the control + */ + ariaLabel?: string; + + /** + * Specify whether the underlying control is checked, + * or not + * @default false + * */ + checked?: boolean; + + /** + * Specify whether the underlying input control should be disabled + * @default false + */ + disabled?: boolean; + + /** + * Provide an `id` for the underlying input control + */ + id: string; + + /** + * Specify whether the control is in an indeterminate state + */ + indeterminate?: boolean; + + /** + * Provide a `name` for the underlying input control + */ + name: string; + + /** + * Provide an optional hook that is called each time the input is updated + */ + onChange?: ( + checked: boolean, + id: string, + event: React.ChangeEvent + ) => void; + + /** + * Provide a handler that is invoked when a user clicks on the control + */ + onClick?: (event: React.MouseEvent) => void; + + /** + * Provide a handler that is invoked on the key down event for the control + */ + onKeyDown?: (event: React.KeyboardEvent) => void; + + /** + * Provide an optional tooltip for the InlineCheckbox + */ + title?: string; +} + +const InlineCheckbox = React.forwardRef( function InlineCheckbox(props, forwardRef) { const { ['aria-label']: ariaLabel, @@ -28,16 +91,19 @@ const InlineCheckbox = React.forwardRef( title, } = props; const prefix = usePrefix(); - const inputRef = useRef(null); + const inputRef = useRef(null); const ref = useMergedRefs([inputRef, forwardRef]); - const inputProps = { + + const inputProps: React.InputHTMLAttributes & { + ref: React.Ref; + } = { checked, className: `${prefix}--checkbox`, disabled, id, name, onClick: onClick ? onClickCheckBoxInput : onClick, - onChange: (evt) => { + onChange: (evt: React.ChangeEvent) => { onChange(evt.target.checked, id, evt); }, onKeyDown, @@ -51,16 +117,15 @@ const InlineCheckbox = React.forwardRef( useEffect(() => { if (inputRef?.current) { - inputRef.current.indeterminate = indeterminate; + inputRef.current.indeterminate = indeterminate || false; } }, [indeterminate]); - function onClickCheckBoxInput(evt) { - // If the previous "indeterminate" is true, change "checked" to false. If it is not undefined, we're working on `TableSelectAll` + function onClickCheckBoxInput(evt: React.MouseEvent) { if (indeterminate) { - evt.target.checked = false; + (evt.target as HTMLInputElement).checked = false; } - onClick(evt); + onClick?.(evt); } return ( @@ -72,7 +137,7 @@ const InlineCheckbox = React.forwardRef( htmlFor={id} className={`${prefix}--checkbox-label`} title={title} - onClick={(evt) => { + onClick={(evt: React.MouseEvent) => { evt.stopPropagation(); }}> diff --git a/packages/react/src/components/InlineCheckbox/index.js b/packages/react/src/components/InlineCheckbox/index.tsx similarity index 61% rename from packages/react/src/components/InlineCheckbox/index.js rename to packages/react/src/components/InlineCheckbox/index.tsx index 4711c79865e8..c159d312a87a 100644 --- a/packages/react/src/components/InlineCheckbox/index.js +++ b/packages/react/src/components/InlineCheckbox/index.tsx @@ -5,7 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -import InlineCheckbox from './InlineCheckbox'; - +import InlineCheckbox, { type InlineCheckboxProps } from './InlineCheckbox'; export default InlineCheckbox; -export { InlineCheckbox }; +export { InlineCheckbox, type InlineCheckboxProps };