CheckboxList and CheckboxControl: Label htmlFor and Input id should be unique (#50566)
* Extract the checkbox list option logic into its own component This allows to generate a dynamic id per option so it never repeats in the entire document. * Add changelog file * Move CheckboxListOptionControl to its own file * Add value to the checkbox control to be aligned with the native input type=checkbox element. And also use it to get the correct checkbox by value in unit tests * Because checkboxes not longer have fixed ids, now we use their value to lookup for them instead * Updating snapshots since the checkboxes ids are now autogenerated
This commit is contained in:
parent
ddbb24e021
commit
640a6ca439
|
@ -125,7 +125,7 @@ const setup = ( params: SetupParams ) => {
|
|||
: [];
|
||||
|
||||
const checkbox = Array.from( checkboxes ).find(
|
||||
( input ) => input.id === value
|
||||
( input ) => input.value === value
|
||||
);
|
||||
|
||||
return checkbox;
|
||||
|
|
|
@ -13,13 +13,14 @@ exports[`Filter by Stock block renders the stock filter block 1`] = `
|
|||
class="wc-block-components-checkbox wc-block-checkbox-list__checkbox"
|
||||
>
|
||||
<label
|
||||
for="instock"
|
||||
for="wc-block-checkbox-list-option-0"
|
||||
>
|
||||
<input
|
||||
aria-invalid="false"
|
||||
class="wc-block-components-checkbox__input"
|
||||
id="instock"
|
||||
id="wc-block-checkbox-list-option-0"
|
||||
type="checkbox"
|
||||
value="instock"
|
||||
/>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
|
@ -44,13 +45,14 @@ exports[`Filter by Stock block renders the stock filter block 1`] = `
|
|||
class="wc-block-components-checkbox wc-block-checkbox-list__checkbox"
|
||||
>
|
||||
<label
|
||||
for="onbackorder"
|
||||
for="wc-block-checkbox-list-option-1"
|
||||
>
|
||||
<input
|
||||
aria-invalid="false"
|
||||
class="wc-block-components-checkbox__input"
|
||||
id="onbackorder"
|
||||
id="wc-block-checkbox-list-option-1"
|
||||
type="checkbox"
|
||||
value="onbackorder"
|
||||
/>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
|
@ -75,13 +77,14 @@ exports[`Filter by Stock block renders the stock filter block 1`] = `
|
|||
class="wc-block-components-checkbox wc-block-checkbox-list__checkbox"
|
||||
>
|
||||
<label
|
||||
for="outofstock"
|
||||
for="wc-block-checkbox-list-option-2"
|
||||
>
|
||||
<input
|
||||
aria-invalid="false"
|
||||
class="wc-block-components-checkbox__input"
|
||||
id="outofstock"
|
||||
id="wc-block-checkbox-list-option-2"
|
||||
type="checkbox"
|
||||
value="outofstock"
|
||||
/>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
|
@ -122,13 +125,14 @@ exports[`Filter by Stock block renders the stock filter block with the filter bu
|
|||
class="wc-block-components-checkbox wc-block-checkbox-list__checkbox"
|
||||
>
|
||||
<label
|
||||
for="instock"
|
||||
for="wc-block-checkbox-list-option-3"
|
||||
>
|
||||
<input
|
||||
aria-invalid="false"
|
||||
class="wc-block-components-checkbox__input"
|
||||
id="instock"
|
||||
id="wc-block-checkbox-list-option-3"
|
||||
type="checkbox"
|
||||
value="instock"
|
||||
/>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
|
@ -153,13 +157,14 @@ exports[`Filter by Stock block renders the stock filter block with the filter bu
|
|||
class="wc-block-components-checkbox wc-block-checkbox-list__checkbox"
|
||||
>
|
||||
<label
|
||||
for="onbackorder"
|
||||
for="wc-block-checkbox-list-option-4"
|
||||
>
|
||||
<input
|
||||
aria-invalid="false"
|
||||
class="wc-block-components-checkbox__input"
|
||||
id="onbackorder"
|
||||
id="wc-block-checkbox-list-option-4"
|
||||
type="checkbox"
|
||||
value="onbackorder"
|
||||
/>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
|
@ -184,13 +189,14 @@ exports[`Filter by Stock block renders the stock filter block with the filter bu
|
|||
class="wc-block-components-checkbox wc-block-checkbox-list__checkbox"
|
||||
>
|
||||
<label
|
||||
for="outofstock"
|
||||
for="wc-block-checkbox-list-option-5"
|
||||
>
|
||||
<input
|
||||
aria-invalid="false"
|
||||
class="wc-block-components-checkbox__input"
|
||||
id="outofstock"
|
||||
id="wc-block-checkbox-list-option-5"
|
||||
type="checkbox"
|
||||
value="outofstock"
|
||||
/>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
|
@ -247,13 +253,14 @@ exports[`Filter by Stock block renders the stock filter block with the product c
|
|||
class="wc-block-components-checkbox wc-block-checkbox-list__checkbox"
|
||||
>
|
||||
<label
|
||||
for="instock"
|
||||
for="wc-block-checkbox-list-option-6"
|
||||
>
|
||||
<input
|
||||
aria-invalid="false"
|
||||
class="wc-block-components-checkbox__input"
|
||||
id="instock"
|
||||
id="wc-block-checkbox-list-option-6"
|
||||
type="checkbox"
|
||||
value="instock"
|
||||
/>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
|
@ -292,13 +299,14 @@ exports[`Filter by Stock block renders the stock filter block with the product c
|
|||
class="wc-block-components-checkbox wc-block-checkbox-list__checkbox"
|
||||
>
|
||||
<label
|
||||
for="onbackorder"
|
||||
for="wc-block-checkbox-list-option-7"
|
||||
>
|
||||
<input
|
||||
aria-invalid="false"
|
||||
class="wc-block-components-checkbox__input"
|
||||
id="onbackorder"
|
||||
id="wc-block-checkbox-list-option-7"
|
||||
type="checkbox"
|
||||
value="onbackorder"
|
||||
/>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
|
@ -337,13 +345,14 @@ exports[`Filter by Stock block renders the stock filter block with the product c
|
|||
class="wc-block-components-checkbox wc-block-checkbox-list__checkbox"
|
||||
>
|
||||
<label
|
||||
for="outofstock"
|
||||
for="wc-block-checkbox-list-option-8"
|
||||
>
|
||||
<input
|
||||
aria-invalid="false"
|
||||
class="wc-block-components-checkbox__input"
|
||||
id="outofstock"
|
||||
id="wc-block-checkbox-list-option-8"
|
||||
type="checkbox"
|
||||
value="outofstock"
|
||||
/>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
|
|
|
@ -135,7 +135,7 @@ const setup = ( params: SetupParams = {} ) => {
|
|||
: [];
|
||||
|
||||
const checkbox = Array.from( checkboxes ).find(
|
||||
( input ) => input.id === value
|
||||
( input ) => input.value === value
|
||||
);
|
||||
|
||||
return checkbox;
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { useInstanceId } from '@wordpress/compose';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { CheckboxControl } from '../checkbox-control';
|
||||
import type { CheckboxListOptions } from './types';
|
||||
|
||||
export type CheckboxListOptionControlProps = {
|
||||
option: CheckboxListOptions;
|
||||
shouldTruncateOptions: boolean;
|
||||
showExpanded: boolean;
|
||||
index: number;
|
||||
limit: number;
|
||||
checked: boolean;
|
||||
disabled: boolean;
|
||||
renderedShowMore: false | JSX.Element;
|
||||
onChange: ( value: string ) => void;
|
||||
};
|
||||
|
||||
export function CheckboxListOptionControl( {
|
||||
option,
|
||||
shouldTruncateOptions,
|
||||
showExpanded,
|
||||
index,
|
||||
limit,
|
||||
checked,
|
||||
disabled,
|
||||
renderedShowMore,
|
||||
onChange,
|
||||
}: CheckboxListOptionControlProps ) {
|
||||
const checkboxControlInstanceId = useInstanceId(
|
||||
CheckboxListOptionControl,
|
||||
'wc-block-checkbox-list-option'
|
||||
) as string;
|
||||
|
||||
return (
|
||||
<>
|
||||
<li
|
||||
{ ...( shouldTruncateOptions &&
|
||||
! showExpanded &&
|
||||
index >= limit && { hidden: true } ) }
|
||||
>
|
||||
<CheckboxControl
|
||||
id={ checkboxControlInstanceId }
|
||||
className="wc-block-checkbox-list__checkbox"
|
||||
label={ option.label }
|
||||
checked={ checked }
|
||||
value={ option.value }
|
||||
onChange={ () => {
|
||||
onChange( option.value );
|
||||
} }
|
||||
disabled={ disabled }
|
||||
/>
|
||||
</li>
|
||||
{ shouldTruncateOptions && index === limit - 1 && renderedShowMore }
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -2,18 +2,15 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import { __, _n, sprintf } from '@wordpress/i18n';
|
||||
import { Fragment, useMemo, useState } from '@wordpress/element';
|
||||
import { useMemo, useState } from '@wordpress/element';
|
||||
import clsx from 'clsx';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { CheckboxListOptionControl } from './checkbox-list-option-control';
|
||||
import type { CheckboxListOptions } from './types';
|
||||
import './style.scss';
|
||||
import { CheckboxControl } from '../checkbox-control';
|
||||
interface CheckboxListOptions {
|
||||
label: React.ReactNode;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface CheckboxListProps {
|
||||
className?: string | undefined;
|
||||
|
@ -126,27 +123,18 @@ const CheckboxList = ( {
|
|||
return (
|
||||
<>
|
||||
{ options.map( ( option, index ) => (
|
||||
<Fragment key={ option.value }>
|
||||
<li
|
||||
{ ...( shouldTruncateOptions &&
|
||||
! showExpanded &&
|
||||
index >= limit && { hidden: true } ) }
|
||||
>
|
||||
<CheckboxControl
|
||||
id={ option.value }
|
||||
className="wc-block-checkbox-list__checkbox"
|
||||
label={ option.label }
|
||||
checked={ checked.includes( option.value ) }
|
||||
onChange={ () => {
|
||||
onChange( option.value );
|
||||
} }
|
||||
disabled={ isDisabled }
|
||||
/>
|
||||
</li>
|
||||
{ shouldTruncateOptions &&
|
||||
index === limit - 1 &&
|
||||
renderedShowMore }
|
||||
</Fragment>
|
||||
<CheckboxListOptionControl
|
||||
key={ option.value }
|
||||
option={ option }
|
||||
shouldTruncateOptions={ shouldTruncateOptions }
|
||||
showExpanded={ showExpanded }
|
||||
index={ index }
|
||||
limit={ limit }
|
||||
checked={ checked.includes( option.value ) }
|
||||
disabled={ isDisabled }
|
||||
renderedShowMore={ renderedShowMore }
|
||||
onChange={ onChange }
|
||||
/>
|
||||
) ) }
|
||||
{ shouldTruncateOptions && renderedShowLess }
|
||||
</>
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
export interface CheckboxListOptions {
|
||||
label: React.ReactNode;
|
||||
value: string;
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: tweak
|
||||
|
||||
Extract the checkbox list option logic into its own component
|
Loading…
Reference in New Issue