Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions biome.jsonc
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
{
"$schema": "https://biomejs.dev/schemas/2.3.8/schema.json",
"files": {
"includes": [
"**/app/**",
"**/src/**",
"**/tests/**",
"!**/node_modules",
"!!**/dist", // Use `!!` to force-ignore since this is a build output directory (recommended by Biome docs)
"!**/src/components/tables/MultiSearch/MultiSearch.tsx",
"!**/tests/installation"
]
},
"formatter": {
"enabled": true,
"indentStyle": "space",
Expand Down Expand Up @@ -28,15 +39,6 @@
"plugins": ["./linter/react-default-props.grit"],
"linter": {
"enabled": true,
"includes": [
"**/app/**",
"**/src/**",
"**/tests/**",
"!**/node_modules",
"!**/dist",
"!**/src/components/tables/MultiSearch/MultiSearch.tsx",
"!**/tests/installation"
],
"rules": {
"recommended": true,
"correctness": {
Expand All @@ -57,6 +59,9 @@
"noUnusedTemplateLiteral": "off",
"noUselessElse": "off"
},
"suspicious": {
"noTsIgnore": "off"
},
"nursery": {}
}
}
Expand Down
1 change: 0 additions & 1 deletion src/components/forms/controls/ComboBox/ComboBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ export const ComboBox = Object.assign(
<MenuProvider
label={label}
items={options}
// biome-ignore lint/a11y/useSemanticElements: False positive: this `role` doesn't directly map to HTML `role`
role="combobox"
triggerAction="focus-interactive" // Keep the dropdown menu open while the input is focused
keyboardInteractions="default" // FIXME
Expand Down
2 changes: 1 addition & 1 deletion src/components/forms/controls/Input/Input.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export const InputDisabled: Story = {
export const InputInvalid: Story = {
args: {
required: true,
pattern: '\d+',
pattern: String.raw`\d+`,
className: 'invalid',
value: 'invalid input',
},
Expand Down
13 changes: 7 additions & 6 deletions src/components/forms/controls/ListBox/ListBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
|* the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import * as React from 'react';
import { mergeRefs, mergeCallbacks } from '../../../../util/reactUtil.ts';
import { mergeRefs, mergeCallbacks, mergeProps } from '../../../../util/reactUtil.ts';
import { classNames as cx, type ComponentProps } from '../../../../util/componentUtil.ts';
import { useScroller } from '../../../../layouts/util/Scroller.tsx';

Expand Down Expand Up @@ -114,7 +114,6 @@ export const Option = (props: OptionProps) => {
const handlePress = React.useCallback(() => { requestSelection(); onSelect?.(); }, [requestSelection, onSelect]);

return (
// biome-ignore lint/a11y/useSemanticElements: Cannot (yet) use `<option>` for this.
<Button
unstyled
id={id}
Expand Down Expand Up @@ -496,10 +495,12 @@ export const ListBox = Object.assign(
<div
{...scrollerProps}
tabIndex={undefined} // Do not make the listbox focusable, use a roving tabindex instead
{...ariaProps}
{...propsRest}
{...listBox.props}
ref={listBoxRef}
{...mergeProps(
ariaProps as Record<string, unknown>,
propsRest,
listBox.props,
{ ref: listBoxRef },
)}
onKeyDown={mergeCallbacks([handleKeyDown, propsRest.onKeyDown, listBox.props.onKeyDown])}
onToggle={mergeCallbacks([props.onToggle, listBox.props.onToggle])}
className={cx(
Expand Down
5 changes: 1 addition & 4 deletions src/components/forms/controls/ListBox/ListBoxStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,7 @@ export const createListBoxStore = <E extends HTMLElement>(_ref: React.RefObject<
// the scroll.
const element = itemTargetRef?.current as undefined | HTMLElement;
if (element && document.activeElement !== element) {
element.focus({
// @ts-ignore Supported in some browsers (e.g. Firefox).
focusVisible: false,
});
element.focus({ focusVisible: false });
}
}
},
Expand Down
26 changes: 13 additions & 13 deletions src/components/forms/controls/ListBoxLazy/ListBoxLazy.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -162,19 +162,19 @@ const ListBoxLazyWithFilterC = (props: ListBoxLazyArgs) => {
}}
/>
{filter !== 'hide' &&
<ListBoxLazy
data-placement="bottom"
{...props}
limit={limit}
pageSize={pageSize}
onUpdateLimit={updateLimit}
virtualItemKeys={virtualItemKeys}
hasMoreItems={hasMoreItems}
isLoading={isLoading}
renderItem={item => <>{itemsFiltered[item.index]?.name}</>}
renderItemLabel={item => itemsFiltered[item.index]?.name ?? 'Unknown'}
placeholderEmpty={items.length === 0 ? 'No items' : 'No items found'}
/>
<ListBoxLazy
data-placement="bottom"
{...props}
limit={limit}
pageSize={pageSize}
onUpdateLimit={updateLimit}
virtualItemKeys={virtualItemKeys}
hasMoreItems={hasMoreItems}
isLoading={isLoading}
renderItem={item => <>{itemsFiltered[item.index]?.name}</>}
renderItemLabel={item => itemsFiltered[item.index]?.name ?? 'Unknown'}
placeholderEmpty={items.length === 0 ? 'No items' : 'No items found'}
/>
}
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ export const Option = (props: OptionProps) => {
}, [toggleSelection, onSelect]);

return (
// biome-ignore lint/a11y/useSemanticElements: Cannot (yet) use `<option>` for this.
<Button
unstyled
id={id}
Expand Down
5 changes: 1 addition & 4 deletions src/components/forms/controls/ListBoxMulti/ListBoxStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,7 @@ export const createListBoxStore = <E extends HTMLElement>(_ref: React.RefObject<
// the scroll.
const element = itemTargetRef?.current as undefined | HTMLElement;
if (element && document.activeElement !== element) {
element.focus({
// @ts-ignore Supported in some browsers (e.g. Firefox).
focusVisible: false,
});
element.focus({ focusVisible: false });
}
}
},
Expand Down
1 change: 0 additions & 1 deletion src/components/forms/controls/Select/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ export const Select = Object.assign(
label={label}
formatItemLabel={formatItemLabel}
items={options}
// biome-ignore lint/a11y/useSemanticElements: False positive: this `role` doesn't directly map to HTML `role`
role="listbox"
keyboardInteractions="form-control"
placement="bottom-start"
Expand Down
1 change: 0 additions & 1 deletion src/components/forms/controls/SelectMulti/SelectMulti.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ export const SelectMulti = Object.assign(
label={label}
formatItemLabel={formatItemLabel}
items={options}
// biome-ignore lint/a11y/useSemanticElements: False positive: this `role` doesn't directly map to HTML `role`
role="listbox"
keyboardInteractions="form-control"
placement="bottom-start"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ type ReactDatePickerIrrelevant = (
| 'placeholderText'
| 'showIcon'
| 'icon'
| 'toggleCalendarOnIconClick'
| 'toggleCalendarOnIconClick'
// Omit the `holidays` prop to prevent a `TS4082` error at declaration generation time because `react-datepicker`
// does not export the `Holiday` type
| 'holidays'
);

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import ReactDatePicker from 'react-datepicker';
import cl from './DateRangePicker.module.scss';


type ReactDatePickerProps = React.ComponentProps<typeof ReactDatePicker>;
// Note: omit the `holidays` prop to prevent a `TS4082` error at declaration generation time because `react-datepicker`
// does not export the `Holiday` type
type ReactDatePickerProps = Omit<React.ComponentProps<typeof ReactDatePicker>, 'holidays'>;

type ReactDatePickerIrrelevant = (
| 'date' // This is in the type, but doesn't seem to actually do anything (use `selected` instead)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const Standard: Story = {};
export const InvalidInput: Story = {
args: {
required: true,
pattern: '\d+',
pattern: String.raw`\d+`,
placeholder: 'Invalid input',
className: 'invalid',
},
Expand Down
10 changes: 3 additions & 7 deletions src/components/overlays/MenuMultiProvider/MenuMultiProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ export const MenuMultiProvider = Object.assign(
floatingStyles,
getReferenceProps,
getFloatingProps,
isMounted,
isOpen,
setIsOpen,
} = useFloatingElement({
Expand Down Expand Up @@ -178,8 +179,6 @@ export const MenuMultiProvider = Object.assign(
// END TEMP
});

const [shouldMountMenu] = useDebounce(isOpen, isOpen ? 0 : 1000);

// biome-ignore lint/correctness/useExhaustiveDependencies: Should not depend on `defaultSelected` (run once only)
const defaultSelectedLabels = React.useMemo((): Map<ItemKey, string> => {
const defaultSelectedKeys: null | Set<ItemKey> = typeof selected !== 'undefined'
Expand Down Expand Up @@ -339,10 +338,7 @@ export const MenuMultiProvider = Object.assign(
const previousActiveElement = previousActiveElementRef.current;

if (previousActiveElement && listBoxElement.matches(':focus-within')) {
previousActiveElement.focus({
// @ts-ignore Supported in some browsers (e.g. Firefox).
focusVisible: false,
});
previousActiveElement.focus({ focusVisible: false });
}
}
}, [action]);
Expand Down Expand Up @@ -436,7 +432,7 @@ export const MenuMultiProvider = Object.assign(
return (
<>
{anchor}
{shouldMountMenu && renderMenu()}
{isMounted && renderMenu()}
</>
);
},
Expand Down
10 changes: 3 additions & 7 deletions src/components/overlays/MenuProvider/MenuProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export const MenuProvider = Object.assign(
floatingStyles,
getReferenceProps,
getFloatingProps,
isMounted,
isOpen,
setIsOpen,
} = useFloatingElement({
Expand Down Expand Up @@ -179,8 +180,6 @@ export const MenuProvider = Object.assign(
// END TEMP
});

const [shouldMountMenu] = useDebounce(isOpen, isOpen ? 0 : 1000);

const renderDefaultSelected = (): null | string => {
const defaultSelectedKey = typeof selected !== 'undefined' ? selected : (defaultSelected ?? null);
return defaultSelectedKey === null ? null : (formatItemLabel?.(defaultSelectedKey) ?? defaultSelectedKey);
Expand Down Expand Up @@ -337,10 +336,7 @@ export const MenuProvider = Object.assign(
const previousActiveElement = previousActiveElementRef.current;

if (previousActiveElement && listBoxElement.matches(':focus-within')) {
previousActiveElement.focus({
// @ts-ignore Supported in some browsers (e.g. Firefox).
focusVisible: false,
});
previousActiveElement.focus({ focusVisible: false });
}
}
}, [action]);
Expand Down Expand Up @@ -439,7 +435,7 @@ export const MenuProvider = Object.assign(
return (
<>
{anchor}
{shouldMountMenu && renderMenu()}
{isMounted && renderMenu()}
</>
);
},
Expand Down
26 changes: 16 additions & 10 deletions src/components/tables/DataTable/DataTableEager.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -359,9 +359,9 @@ export const DataTableEagerWithPageLayout: Story = {

// Edge cases: table within table and table within modal within table

type LoremIpsum = {
lorem: string | LoremIpsum,
ipsum: string | LoremIpsum,
type DataTableEagerEdgeCasesInnerTestData = {
lorem: string | DataTableEagerEdgeCasesInnerTestData,
ipsum: string | DataTableEagerEdgeCasesInnerTestData,
};
const loremIpsumItems = [
{ lorem: 'lorem', ipsum: 'ipsum' },
Expand All @@ -371,7 +371,7 @@ const DataTableEagerEdgeCasesInnerTemplate = () => {
const innerColumns = [
{
id: 'lorem',
accessor: (data: LoremIpsum) => data.lorem,
accessor: (data: DataTableEagerEdgeCasesInnerTestData) => data.lorem,
Header: 'Lorem',
Cell: ({ value }: { value: string }) => value,
disableSortBy: false,
Expand All @@ -380,7 +380,7 @@ const DataTableEagerEdgeCasesInnerTemplate = () => {
},
{
id: 'ipsum',
accessor: (data: LoremIpsum) => data.ipsum,
accessor: (data: DataTableEagerEdgeCasesInnerTestData) => data.ipsum,
Header: 'Ipsum',
Cell: ({ value }: { value: string }) => value,
disableSortBy: false,
Expand Down Expand Up @@ -412,11 +412,13 @@ const ModalButton = () => {
</DialogModal>
);
};
const DataTableEagerEdgeCasesTemplate = (props: DataTableEager.TableProviderEagerProps<LoremIpsum>) => {
const DataTableEagerEdgeCasesTemplate = (
props: DataTableEager.TableProviderEagerProps<DataTableEagerEdgeCasesInnerTestData>,
) => {
const columns = [
{
id: 'innertable',
accessor: (data: LoremIpsum) => data.lorem,
accessor: (data: DataTableEagerEdgeCasesInnerTestData) => data.lorem,
Header: 'Inner table',
Cell: () => <DataTableEagerEdgeCasesInnerTemplate/>,
disableSortBy: false,
Expand All @@ -425,7 +427,7 @@ const DataTableEagerEdgeCasesTemplate = (props: DataTableEager.TableProviderEage
},
{
id: 'modalbutton',
accessor: (data: LoremIpsum) => data.ipsum,
accessor: (data: DataTableEagerEdgeCasesInnerTestData) => data.ipsum,
Header: 'Modal Button',
Cell: () => <ModalButton/>,
disableSortBy: false,
Expand All @@ -451,11 +453,15 @@ export const DataTableEagerWithPageLayoutEdgeCases: StoryObj<typeof DataTableEag
args: {
items: loremIpsumItems,
},
render: (args: DataTableEager.TableProviderEagerProps<LoremIpsum>) => <DataTableEagerEdgeCasesTemplate {...args} />,
render: (
args: DataTableEager.TableProviderEagerProps<DataTableEagerEdgeCasesInnerTestData>,
) => <DataTableEagerEdgeCasesTemplate {...args} />,
decorators: [
Story => (
<PageLayout>
<PageLayout.Header title={<PageLayout.Heading>PageLayout with edgeless parameter - edge cases</PageLayout.Heading>}/>
<PageLayout.Header
title={<PageLayout.Heading>PageLayout with edgeless parameter - edge cases</PageLayout.Heading>}
/>
<PageLayout.Body edgeless={true}>
<Story/>
</PageLayout.Body>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export const useRowSelectColumnRadio = <D extends object>(hooks: ReactTable.Hook

const handleRadioChange = () => {
// deselect all other rows first (mimic radio button behavior)
cellProps.rows.forEach(row => row.toggleRowSelected(false));
cellProps.rows.forEach(row => { row.toggleRowSelected(false); });
// then select this row
onChange?.({ target: { checked: true } } as any);
};
Expand Down
Loading