Skip to content

Commit

Permalink
feat: boolean visual state attributes (#3407)
Browse files Browse the repository at this point in the history
  • Loading branch information
mshatikhin committed May 2, 2024
1 parent 4bf4d89 commit 132a177
Show file tree
Hide file tree
Showing 16 changed files with 84 additions and 35 deletions.
2 changes: 1 addition & 1 deletion packages/react-ui/components/Calendar/DayCellView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { ThemeContext } from '../../lib/theming/ThemeContext';
import { cx } from '../../lib/theming/Emotion';
import { isTheme2022 } from '../../lib/theming/ThemeHelpers';
import { DatePickerLocaleHelper } from '../DatePicker/locale';
import { getVisualStateDataAttributes } from '../../internal/CommonWrapper/getVisualStateDataAttributes';
import { getVisualStateDataAttributes } from '../../internal/CommonWrapper/utils/getVisualStateDataAttributes';

import * as CDS from './CalendarDateShape';
import { globalClasses, styles } from './DayCellView.styles';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { ComboBox } from '../ComboBox';
import { InputLikeTextDataTids } from '../../../internal/InputLikeText';
import { MenuItem, MenuItemDataTids } from '../../MenuItem';
import { MenuDataTids } from '../../../internal/Menu';
import { delay, clickOutside } from '../../../lib/utils';
import { clickOutside, delay } from '../../../lib/utils';
import { ComboBoxMenuDataTids, DELAY_BEFORE_SHOW_LOADER, LOADER_SHOW_TIME } from '../../../internal/CustomComboBox';
import { ComboBoxViewIds } from '../../../internal/CustomComboBox/ComboBoxView';
import { SpinnerDataTids } from '../../Spinner';
Expand Down Expand Up @@ -416,7 +416,7 @@ describe('ComboBox', () => {
await promise;

const menuItems = screen.getAllByTestId(ComboBoxMenuDataTids.item);
expect(menuItems.find((element) => element.getAttribute('data-visual-state-hover') === 'true')).toBeFalsy();
expect(menuItems.find((element) => element.getAttribute('data-visual-state-hover') === '')).toBeFalsy();
});

it('highlights menu item on focus with non-empty input', async () => {
Expand All @@ -430,7 +430,6 @@ describe('ComboBox', () => {
const menuItems = screen.getAllByTestId(ComboBoxMenuDataTids.item);
expect(menuItems.find((element) => element.hasAttribute('data-visual-state-hover'))).toHaveAttribute(
'data-visual-state-hover',
'true',
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,8 @@ describe('<DropdownMenu />', () => {
userEvent.keyboard('{arrowdown}');
userEvent.keyboard('{arrowdown}');
await delay(0);
expect(screen.getByTestId('menuItem2')).not.toHaveAttribute('data-visual-state-hover', 'true');
expect(screen.getByTestId('menuItem3')).toHaveAttribute('data-visual-state-hover', 'true');
expect(screen.getByTestId('menuItem2')).not.toHaveAttribute('data-visual-state-hover');
expect(screen.getByTestId('menuItem3')).toHaveAttribute('data-visual-state-hover');
});

it("doesn't click on a not selectable MenuItem", async () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/react-ui/components/MenuItem/MenuItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { rootNode, TSetRootNode } from '../../lib/rootNode';
import { SizeProp } from '../../lib/types/props';
import { MenuContext, MenuContextType } from '../../internal/Menu/MenuContext';
import { getFullReactUIFlagsContext, ReactUIFeatureFlagsContext } from '../../lib/featureFlagsContext';
import { getVisualStateDataAttributes } from '../../internal/CommonWrapper/getVisualStateDataAttributes';
import { getVisualStateDataAttributes } from '../../internal/CommonWrapper/utils/getVisualStateDataAttributes';

import { styles } from './MenuItem.styles';

Expand Down
4 changes: 2 additions & 2 deletions packages/react-ui/components/Paging/Paging.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ import { isIE11 } from '../../lib/client';
import { ThemeContext } from '../../lib/theming/ThemeContext';
import { Theme } from '../../lib/theming/Theme';
import { ArrowChevronRightIcon } from '../../internal/icons/16px';
import { CommonWrapper, CommonProps } from '../../internal/CommonWrapper';
import { CommonProps, CommonWrapper } from '../../internal/CommonWrapper';
import { cx } from '../../lib/theming/Emotion';
import { rootNode, TSetRootNode } from '../../lib/rootNode';
import { createPropsGetter } from '../../lib/createPropsGetter';
import { isTheme2022 } from '../../lib/theming/ThemeHelpers';
import { getVisualStateDataAttributes } from '../../internal/CommonWrapper/getVisualStateDataAttributes';
import { getVisualStateDataAttributes } from '../../internal/CommonWrapper/utils/getVisualStateDataAttributes';

import { styles } from './Paging.styles';
import * as NavigationHelper from './NavigationHelper';
Expand Down
12 changes: 6 additions & 6 deletions packages/react-ui/components/Select/__tests__/Select-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ describe('Select', () => {
expect(screen.getByTestId(MenuDataTids.root)).toBeInTheDocument();

const menuItems = screen.getAllByTestId(MenuItemDataTids.root);
const selectedMenuItem = menuItems.find((element) => element.getAttribute('data-visual-state-selected') === 'true');
const selectedMenuItem = menuItems.find((element) => element.getAttribute('data-visual-state-selected') === '');
expect(selectedMenuItem).toBeInTheDocument();
expect(selectedMenuItem).toHaveTextContent(currentValueText);
});
Expand Down Expand Up @@ -444,15 +444,15 @@ describe('Select', () => {
expect(screen.getByTestId(MenuDataTids.root)).toBeInTheDocument();
const menuItems = screen.getAllByTestId(MenuItemDataTids.root);

expect(menuItems.find((element) => element.getAttribute('data-visual-state-hover') === 'true')).toBeFalsy();
expect(menuItems.find((element) => element.getAttribute('data-visual-state-hover') === '')).toBeFalsy();
userEvent.keyboard('{arrowdown}');

expect(menuItems.find((element) => element.getAttribute('data-visual-state-hover') === 'true')).toHaveTextContent(
expect(menuItems.find((element) => element.getAttribute('data-visual-state-hover') === '')).toHaveTextContent(
testItems[0],
);

userEvent.keyboard('{arrowdown}');
expect(menuItems.find((element) => element.getAttribute('data-visual-state-hover') === 'true')).toHaveTextContent(
expect(menuItems.find((element) => element.getAttribute('data-visual-state-hover') === '')).toHaveTextContent(
testItems[1],
);
});
Expand All @@ -463,10 +463,10 @@ describe('Select', () => {
expect(screen.getByTestId(MenuDataTids.root)).toBeInTheDocument();
const menuItems = screen.getAllByTestId(MenuItemDataTids.root);

expect(menuItems.find((element) => element.getAttribute('data-visual-state-hover') === 'true')).toBeFalsy();
expect(menuItems.find((element) => element.getAttribute('data-visual-state-hover') === '')).toBeFalsy();
userEvent.keyboard('{arrowup}');

expect(menuItems.find((element) => element.getAttribute('data-visual-state-hover') === 'true')).toHaveTextContent(
expect(menuItems.find((element) => element.getAttribute('data-visual-state-hover') === '')).toHaveTextContent(
testItems[testItems.length - 1],
);
});
Expand Down
6 changes: 3 additions & 3 deletions packages/react-ui/components/Tabs/Tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import { cx } from '../../lib/theming/Emotion';
import { rootNode, TSetRootNode } from '../../lib/rootNode';
import { createPropsGetter } from '../../lib/createPropsGetter';
import { SizeProp } from '../../lib/types/props';
import { getVisualStateDataAttributes } from '../../internal/CommonWrapper/getVisualStateDataAttributes';
import { getVisualStateDataAttributes } from '../../internal/CommonWrapper/utils/getVisualStateDataAttributes';

import { TabsContext, TabsContextType, TabsContextDefaultValue } from './TabsContext';
import { styles, horizontalStyles, verticalStyles, globalClasses } from './Tab.styles';
import { TabsContext, TabsContextDefaultValue, TabsContextType } from './TabsContext';
import { globalClasses, horizontalStyles, styles, verticalStyles } from './Tab.styles';

export interface TabIndicators {
error: boolean;
Expand Down
4 changes: 2 additions & 2 deletions packages/react-ui/internal/CommonWrapper/CommonWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { getRootNode, isInstanceWithRootNode, rootNode, TRootNodeSubscription, T
import { callChildRef } from '../../lib/callChildRef/callChildRef';

import type { CommonProps, CommonPropsRootNodeRef, CommonWrapperProps } from './types';
import { extractCommonProps } from './extractCommonProps';
import { getCommonVisualStateDataAttributes } from './getCommonVisualStateDataAttributes';
import { extractCommonProps } from './utils/extractCommonProps';
import { getCommonVisualStateDataAttributes } from './utils/getCommonVisualStateDataAttributes';

export type CommonPropsWithRootNodeRef = CommonProps & CommonPropsRootNodeRef;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import { render, screen } from '@testing-library/react';

import { CommonWrapper } from '../CommonWrapper';

describe('CommonWrapper', () => {
it('common cases', () => {
render(
<CommonWrapper
data-tid="CommonWrapper"
data-selected
disabled
warning
error
hover={false}
className="className"
style={{ padding: 10 }}
>
<div>Children</div>
</CommonWrapper>,
);

const element = screen.getByText('Children');
expect(element).toHaveClass('className');
expect(element).toHaveStyle('padding: 10px;');
expect(element).toHaveAttribute('data-tid', 'CommonWrapper');
expect(element).toHaveAttribute('data-selected', 'true');
expect(element).toHaveAttribute('data-visual-state-error');
expect(element).toHaveAttribute('data-visual-state-warning');
expect(element).not.toHaveAttribute('data-visual-state-disabled');
expect(element).not.toHaveAttribute('data-visual-state-hover');
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getCommonVisualStateDataAttributes } from '../getCommonVisualStateDataAttributes';
import { getCommonVisualStateDataAttributes } from '../utils/getCommonVisualStateDataAttributes';

describe('getCommonVisualStateDataAttributes', () => {
it('empty object', () => {
Expand All @@ -9,8 +9,7 @@ describe('getCommonVisualStateDataAttributes', () => {
expect(
getCommonVisualStateDataAttributes({ skip: true, error: true, warning: false, disabled: true }),
).toStrictEqual({
'data-visual-state-error': 'true',
'data-visual-state-warning': 'false',
'data-visual-state-error': '',
});
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getVisualStateDataAttributes } from '../getVisualStateDataAttributes';
import { getVisualStateDataAttributes } from '../utils/getVisualStateDataAttributes';

describe('getVisualStateDataAttributes', () => {
it('empty object', () => {
Expand All @@ -10,10 +10,11 @@ describe('getVisualStateDataAttributes', () => {
getVisualStateDataAttributes({
attributeNull: null,
attributeUndefined: undefined,
attributeBoolean: true,
attributeBooleanFalse: false,
attributeBooleanTrue: true,
}),
).toStrictEqual({
'data-visual-state-attribute-boolean': 'true',
'data-visual-state-attribute-boolean-true': '',
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { tryGetBoolean } from '../utils/tryGetBoolean';

describe('tryGetBoolean', () => {
it('should return true when passed true', () => {
expect(tryGetBoolean(true)).toBe(true);
});

it('should return false when passed false', () => {
expect(tryGetBoolean(false)).toBe(false);
});
});

describe('tryGetBoolean with invalid input', () => {
it.each([null, undefined, 42, 'hello', {}, []])('should return undefined when passed `%s`', (value) => {
expect(tryGetBoolean(value)).toBeUndefined();
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { NotCommonProps } from './types';
import type { CommonPropsWithRootNodeRef } from './CommonWrapper';
import type { NotCommonProps } from '../types';
import type { CommonPropsWithRootNodeRef } from '../CommonWrapper';

export const extractCommonProps = <P extends CommonPropsWithRootNodeRef>(
props: P,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { getVisualStateDataAttributes, VisualStateDataAttributesResultType } from './getVisualStateDataAttributes';

const tryGetBoolean = (value: unknown): boolean | undefined => (typeof value === 'boolean' ? value : undefined);
import { tryGetBoolean } from './tryGetBoolean';

export function getCommonVisualStateDataAttributes(
componentProps: Record<string, unknown>,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { toKebabCase } from '../../lib/toKebabCase';
import type { Nullable } from '../../typings/utility-types';
import { toKebabCase } from '../../../lib/toKebabCase';
import { Nullable } from '../../../typings/utility-types';

export type VisualStateDataAttributesResultType = Record<string, string>;

const prefix = `data-visual-state-`;

function fillResult(result: VisualStateDataAttributesResultType, key: string, value: unknown) {
if (value !== null && value !== undefined) {
result[`${prefix}${toKebabCase(key)}`] = String(value);
function fillResult(result: VisualStateDataAttributesResultType, key: string, value: Nullable<boolean>) {
if (value) {
result[`${prefix}${toKebabCase(key)}`] = '';
}

return result;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const tryGetBoolean = (value: unknown): boolean | undefined => (typeof value === 'boolean' ? value : undefined);

0 comments on commit 132a177

Please sign in to comment.