Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
7 changes: 7 additions & 0 deletions .changeset/value-mods.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@cube-dev/ui-kit': major
---

**BREAKING:** Boolean mods now generate `data-*` instead of `data-is-*` attributes (`mods={{ hovered: true }}``data-hovered=""` instead of `data-is-hovered=""`).

**NEW:** Value mods support - `mods` now accepts string values (`mods={{ theme: 'danger' }}``data-theme="danger"`). Includes shorthand syntax in styles (`theme=danger`, `theme="danger"`). See Tasty documentation for details.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ coverage
size-limit-report/stats.json
docs-static
*.plan.md
*.spec.md
8 changes: 4 additions & 4 deletions src/components/actions/Button/button.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,20 @@ describe('<Button />', () => {
expect(screen.getByTestId('test')).toBeInTheDocument();
});

it('should have data-is-loading', () => {
it('should have data-loading', () => {
render(
<Button isLoading data-qa="ApplyDbConnection">
Apply
</Button>,
);

expect(screen.getByTestId('ApplyDbConnection')).toHaveAttribute(
'data-is-loading',
'data-loading',
'',
);
});

it('should have data-is-loading after rerender', () => {
it('should have data-loading after rerender', () => {
const { rerender } = render(
<Button isLoading={false} data-qa="ApplyDbConnection">
Apply
Expand All @@ -38,7 +38,7 @@ describe('<Button />', () => {
);

expect(screen.getByTestId('ApplyDbConnection')).toHaveAttribute(
'data-is-loading',
'data-loading',
'',
);
});
Expand Down
8 changes: 4 additions & 4 deletions src/components/actions/CommandMenu/CommandMenu.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -859,7 +859,7 @@ describe('CommandMenu', () => {
);

const commandMenu = screen.getByTestId('test-command-menu');
expect(commandMenu).toHaveAttribute('data-is-popover');
expect(commandMenu).toHaveAttribute('data-popover');
});

it('should apply tray mod when used with MenuTrigger', () => {
Expand All @@ -875,7 +875,7 @@ describe('CommandMenu', () => {
);

const commandMenu = screen.getByTestId('test-command-menu');
expect(commandMenu).toHaveAttribute('data-is-tray');
expect(commandMenu).toHaveAttribute('data-tray');
});

it('should not apply any special mods when used standalone', () => {
Expand All @@ -887,8 +887,8 @@ describe('CommandMenu', () => {
);

const commandMenu = screen.getByTestId('test-command-menu');
expect(commandMenu).not.toHaveAttribute('data-is-popover');
expect(commandMenu).not.toHaveAttribute('data-is-tray');
expect(commandMenu).not.toHaveAttribute('data-popover');
expect(commandMenu).not.toHaveAttribute('data-tray');
});
});
});
16 changes: 8 additions & 8 deletions src/components/actions/Menu/Menu.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ describe('<Menu />', () => {
);

// With first strategy, first menu item gets focus
const focusedItem = container.querySelector('[data-is-focused]');
const focusedItem = container.querySelector('[data-focused]');
expect(focusedItem).toBeInTheDocument();
});

Expand All @@ -568,7 +568,7 @@ describe('<Menu />', () => {
);

// With last strategy, last menu item gets focus
const focusedItem = container.querySelector('[data-is-focused]');
const focusedItem = container.querySelector('[data-focused]');
expect(focusedItem).toBeInTheDocument();
});

Expand Down Expand Up @@ -646,12 +646,12 @@ describe('<Menu />', () => {

// Check wrapper mods (header, footer are on wrapper)
const menuWrapper = container.querySelector('[data-qa="Menu"]');
expect(menuWrapper).toHaveAttribute('data-is-header', '');
expect(menuWrapper).toHaveAttribute('data-is-footer', '');
expect(menuWrapper).toHaveAttribute('data-header', '');
expect(menuWrapper).toHaveAttribute('data-footer', '');

// Check menu list mods (sections mod is on the menu list)
const menu = getByRole('menu');
expect(menu).toHaveAttribute('data-is-sections', '');
expect(menu).toHaveAttribute('data-sections', '');
});

// Ref tests
Expand Down Expand Up @@ -1048,7 +1048,7 @@ describe('Menu popover mod', () => {
);

const menuWrapper = container.querySelector('[data-qa="Menu"]');
expect(menuWrapper).toHaveAttribute('data-is-popover');
expect(menuWrapper).toHaveAttribute('data-popover');
});

it('should not apply popover mod when used standalone', () => {
Expand All @@ -1060,7 +1060,7 @@ describe('Menu popover mod', () => {
);

const menuWrapper = container.querySelector('[data-qa="Menu"]');
expect(menuWrapper).not.toHaveAttribute('data-is-popover');
expect(menuWrapper).not.toHaveAttribute('data-popover');
});

it('should not apply popover mod when context provides false', () => {
Expand All @@ -1076,7 +1076,7 @@ describe('Menu popover mod', () => {
);

const menuWrapper = container.querySelector('[data-qa="Menu"]');
expect(menuWrapper).not.toHaveAttribute('data-is-popover');
expect(menuWrapper).not.toHaveAttribute('data-popover');
});
});

Expand Down
16 changes: 8 additions & 8 deletions src/components/actions/Menu/SubMenuTrigger.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ describe('<SubMenuTrigger />', () => {
// Verify Share item is focused before trying to open submenu
await waitFor(() => {
const shareItem = getByText('Share').closest('li');
expect(shareItem).toHaveAttribute('data-is-focused');
expect(shareItem).toHaveAttribute('data-focused');
});

await act(async () => {
Expand Down Expand Up @@ -221,7 +221,7 @@ describe('<SubMenuTrigger />', () => {
// Verify Share item is focused before trying to open submenu
await waitFor(() => {
const shareItem = getByText('Share').closest('li');
expect(shareItem).toHaveAttribute('data-is-focused');
expect(shareItem).toHaveAttribute('data-focused');
});

await act(async () => {
Expand Down Expand Up @@ -279,7 +279,7 @@ describe('<SubMenuTrigger />', () => {
// Verify Share item is focused before trying to open submenu
await waitFor(() => {
const shareItem = getByText('Share').closest('li');
expect(shareItem).toHaveAttribute('data-is-focused');
expect(shareItem).toHaveAttribute('data-focused');
});

await act(async () => {
Expand Down Expand Up @@ -337,7 +337,7 @@ describe('<SubMenuTrigger />', () => {
// Verify Share item is focused before trying to open submenu
await waitFor(() => {
const shareItem = getByText('Share').closest('li');
expect(shareItem).toHaveAttribute('data-is-focused');
expect(shareItem).toHaveAttribute('data-focused');
});

await act(async () => {
Expand Down Expand Up @@ -401,7 +401,7 @@ describe('<SubMenuTrigger />', () => {
// Verify Share item is focused before trying to open submenu
await waitFor(() => {
const shareItem = getByText('Share').closest('li');
expect(shareItem).toHaveAttribute('data-is-focused');
expect(shareItem).toHaveAttribute('data-focused');
});

await act(async () => {
Expand Down Expand Up @@ -713,7 +713,7 @@ describe('<SubMenuTrigger />', () => {
// Verify Share item is focused before trying to open submenu
await waitFor(() => {
const shareItem = getByText('Share').closest('li');
expect(shareItem).toHaveAttribute('data-is-focused');
expect(shareItem).toHaveAttribute('data-focused');
});

await act(async () => {
Expand All @@ -731,9 +731,9 @@ describe('<SubMenuTrigger />', () => {
// The actual DOM focus might be on the menu itself with aria-activedescendant
const firstSubmenuItem = getByText('Copy link').closest('li');

// Check for focus indication - tasty sets data-is-focused based on mods.focused
// Check for focus indication - tasty sets data-focused based on mods.focused
await waitFor(() => {
expect(firstSubmenuItem).toHaveAttribute('data-is-focused');
expect(firstSubmenuItem).toHaveAttribute('data-focused');
});
});
});
Expand Down
6 changes: 3 additions & 3 deletions src/components/content/ActiveZone/ActiveZone.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ function ActiveZone(

return (
<Element
data-is-hovered={isHovered && !isDisabled ? '' : null}
data-is-focused={isFocused && !isDisabled ? '' : null}
data-is-disabled={isDisabled || null}
data-hovered={isHovered && !isDisabled ? '' : null}
data-focused={isFocused && !isDisabled ? '' : null}
data-disabled={isDisabled || null}
aria-label={label}
{...mergeProps(
hoverProps,
Expand Down
65 changes: 24 additions & 41 deletions src/components/content/Item/Item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -778,43 +778,30 @@ const Item = <T extends HTMLElement = HTMLDivElement>(
[hotkeys, finalIsDisabled],
);

mods = useMemo(() => {
return {
'with-icon': !!finalIcon,
'with-right-icon': !!finalRightIcon,
'with-label': !!(children || labelProps),
'with-prefix': !!finalPrefix,
'with-suffix': !!finalSuffix,
'with-description': showDescriptions,
'with-description-block':
showDescriptions && descriptionPlacement === 'block',
'with-actions': !!actions,
'with-actions-content': !!(actions && actions !== true),
checkbox: hasCheckbox,
disabled: finalIsDisabled,
selected: isSelected === true,
loading: isLoading,
card: isCard === true,
button: isButton === true,
[`shape-${shape}`]: true,
...mods,
};
}, [
finalIcon,
finalRightIcon,
finalPrefix,
finalSuffix,
showDescriptions,
descriptionPlacement,
hasCheckbox,
isSelected,
isLoading,
isCard,
isButton,
shape,
actions,
mods,
]);
mods = {
'with-icon': !!finalIcon,
'with-right-icon': !!finalRightIcon,
'with-label': !!(children || labelProps),
'with-prefix': !!finalPrefix,
'with-suffix': !!finalSuffix,
'with-description': showDescriptions,
'with-description-block':
showDescriptions && descriptionPlacement === 'block',
'with-actions': !!actions,
'with-actions-content': !!(actions && actions !== true),
checkbox: hasCheckbox,
disabled: finalIsDisabled,
selected: isSelected === true,
loading: isLoading,
card: isCard === true,
button: isButton === true,
[`shape-${shape}`]: true,
// Use value mods for data-* attributes
size: typeof size === 'number' ? undefined : size,
type,
theme,
...mods,
};

const {
labelProps: finalLabelProps,
Expand Down Expand Up @@ -860,10 +847,6 @@ const Item = <T extends HTMLElement = HTMLDivElement>(
theme && type ? (`${theme}.${type}` as ItemVariant) : undefined
}
disabled={finalIsDisabled}
data-size={typeof size === 'number' ? undefined : size}
data-type={type}
data-theme={theme}
data-shape={shape}
aria-disabled={finalIsDisabled}
aria-selected={isSelected}
mods={mods}
Expand Down
4 changes: 2 additions & 2 deletions src/components/fields/ComboBox/ComboBox.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -839,7 +839,7 @@ describe('<ComboBox />', () => {
);

await waitFor(() => {
const wrapper = combobox.closest('[data-is-invalid]');
const wrapper = combobox.closest('[data-invalid]');
expect(wrapper).toBeInTheDocument();
});

Expand All @@ -852,7 +852,7 @@ describe('<ComboBox />', () => {
);

await waitFor(() => {
const wrapper = combobox.closest('[data-is-valid]');
const wrapper = combobox.closest('[data-valid]');
expect(wrapper).toBeInTheDocument();
});

Expand Down
4 changes: 2 additions & 2 deletions src/components/fields/FilterListBox/FilterListBox.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -800,15 +800,15 @@ describe('<FilterListBox />', () => {
</FilterListBox>,
);

expect(container.firstChild).toHaveAttribute('data-is-valid');
expect(container.firstChild).toHaveAttribute('data-valid');

rerender(
<FilterListBox label="Select a fruit" validationState="invalid">
{basicItems}
</FilterListBox>,
);

expect(container.firstChild).toHaveAttribute('data-is-invalid');
expect(container.firstChild).toHaveAttribute('data-invalid');
});
});

Expand Down
2 changes: 1 addition & 1 deletion src/components/fields/FilterListBox/FilterListBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -815,7 +815,7 @@ export const FilterListBox = forwardRef(function FilterListBox<
ref={searchInputRef}
qa={qa || 'FilterListBox'}
id={id}
data-is-prefix={isLoading ? '' : undefined}
data-prefix={isLoading ? '' : undefined}
type="search"
placeholder={searchPlaceholder}
value={searchValue}
Expand Down
Loading
Loading