Skip to content

refactor: use css outline & new useFocusVisibleClassName() hook to draw visible focus #5876

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
9c4a9b7
fix(SegmentedControlOption): fix hover state
eugpoloz Nov 20, 2023
aec879e
feat(hooks): create useFocusVisibleClassName()
eugpoloz Nov 20, 2023
35395c5
test(hooks): useFocusVisibleClassName()
eugpoloz Nov 20, 2023
7f835ba
refactor(SegmentedControl): useFocusVisibleClassName()
eugpoloz Nov 20, 2023
d1f8c5b
refactor(Clickable): useFocusVisibleClassName()
eugpoloz Nov 20, 2023
a9140a3
refactor(FormField): useFocusVisibleClassName()
eugpoloz Nov 20, 2023
96e5701
refactor(Tappable): useFocusVisibleClassName()
eugpoloz Nov 20, 2023
010cb83
e2e(Tappable): add focus visible state visual test
eugpoloz Nov 20, 2023
4493895
refactor(Link): useFocusVisibleClassName()
eugpoloz Nov 20, 2023
f514a21
e2e(Link): add focus visible state visual test
eugpoloz Nov 20, 2023
fed15dd
refactor(Slider): useFocusVisibleClassName()
eugpoloz Nov 20, 2023
9897746
e2e(Slider): adjust visual test for reduced motion
eugpoloz Nov 20, 2023
04e41b2
refactor(Switch): useFocusVisibleClassName()
eugpoloz Nov 20, 2023
258824c
e2e(Switch): add focus visible state visual test
eugpoloz Nov 20, 2023
a9b4151
e2e(Textarea): update test & add focus state
eugpoloz Nov 20, 2023
5065bea
e2e(CustomSelect): update screenshots
eugpoloz Nov 20, 2023
99dd9b7
refactor(Image): useFocusVisibleClassName()
eugpoloz Nov 21, 2023
a4c04c6
e2e(Image): add focus visible state visual tests
eugpoloz Nov 21, 2023
a16a1a5
fix(css): reset outline the smart way
eugpoloz Nov 20, 2023
d1690ec
refactor: rm FocusVisible component
eugpoloz Nov 20, 2023
7604fca
test(hooks): useFocusVisibleClassName()
eugpoloz Nov 22, 2023
b69bef6
fix(focusVisible): adjust css classes
eugpoloz Nov 23, 2023
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
28 changes: 15 additions & 13 deletions packages/vkui/src/components/Avatar/Avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,37 +81,39 @@ export const Avatar = ({
className,
gradientColor,
initials,
fallbackIcon,
fallbackIcon: fallbackIconProp,
children,
...restProps
}: AvatarProps) => {
const gradientName =
typeof gradientColor === 'number' ? COLORS_NUMBER_TO_TEXT_MAP[gradientColor] : gradientColor;
const isGradientNotCustom = gradientName && gradientName !== 'custom';
const rewrittenFallbackIcon = initials ? undefined : fallbackIcon;

const fallbackIcon = initials ? (
<div
className={styles['Avatar__initials']}
style={{
fontSize: getInitialsFontSize(size),
}}
>
{initials}
</div>
) : (
fallbackIconProp
);

return (
<ImageBase
{...restProps}
size={size}
fallbackIcon={rewrittenFallbackIcon}
fallbackIcon={fallbackIcon}
className={classNames(
styles['Avatar'],
gradientName && styles['Avatar--has-gradient'],
isGradientNotCustom && gradientStyles[gradientName],
className,
)}
>
{initials && (
<div
className={styles['Avatar__initials']}
style={{
fontSize: getInitialsFontSize(size),
}}
>
{initials}
</div>
)}
{children}
</ImageBase>
);
Expand Down
5 changes: 0 additions & 5 deletions packages/vkui/src/components/Clickable/Clickable.module.css
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
.Clickable__host {
cursor: pointer;
}

.Clickable__host:focus,
.Clickable__host:focus-visible {
outline: none;
}
12 changes: 7 additions & 5 deletions packages/vkui/src/components/Clickable/Clickable.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import * as React from 'react';
import { classNames } from '@vkontakte/vkjs';
import { useFocusVisible } from '../../hooks/useFocusVisible';
import {
type FocusVisibleModeProps,
useFocusVisibleClassName,
} from '../../hooks/useFocusVisibleClassName';
import { callMultiple } from '../../lib/callMultiple';
import { FocusVisible, FocusVisibleMode } from '../FocusVisible/FocusVisible';
import { RootComponent, RootComponentProps } from '../RootComponent/RootComponent';
import styles from './Clickable.module.css';

export interface ClickableProps<T> extends RootComponentProps<T> {
export interface ClickableProps<T> extends RootComponentProps<T>, FocusVisibleModeProps {
baseClassName?: string;
focusVisibleMode?: FocusVisibleMode;
}

/**
Expand All @@ -31,16 +33,16 @@ const RealClickable = <T,>({
...restProps
}: ClickableProps<T>) => {
const { focusVisible, onBlur, onFocus } = useFocusVisible();
const focusVisibleClassNames = useFocusVisibleClassName({ focusVisible, mode: focusVisibleMode });

return (
<RootComponent
baseClassName={classNames(baseClassName, styles['Clickable__host'])}
baseClassName={classNames(baseClassName, focusVisibleClassNames, styles['Clickable__host'])}
onBlur={callMultiple(onBlur, restProps.onBlur)}
onFocus={callMultiple(onFocus, restProps.onFocus)}
{...restProps}
>
{children}
<FocusVisible visible={focusVisible} mode={focusVisibleMode} />
</RootComponent>
);
};
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

This file was deleted.

35 changes: 0 additions & 35 deletions packages/vkui/src/components/FocusVisible/FocusVisible.tsx

This file was deleted.

18 changes: 11 additions & 7 deletions packages/vkui/src/components/FormField/FormField.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
-webkit-tap-highlight-color: transparent;
isolation: isolate;
border-radius: var(--vkui--size_border_radius--regular);
outline: none;
}

.FormField--sizeY-compact {
Expand Down Expand Up @@ -86,9 +85,9 @@
}

/**
* [start]
* CMP:
* FormItem
* [start]
*/
:global(.vkuiInternalFormItem--status-error) .FormField__border,
.FormField--status-error .FormField__border {
Expand Down Expand Up @@ -123,11 +122,6 @@
z-index: var(--vkui_internal--z_index_form_field_border_hover);
}

/* stylelint-disable-next-line @project-tools/stylelint-atomic, selector-max-universal */
.FormField *:focus {
outline: none;
}

/**
* CMP:
* ModalCardBase
Expand Down Expand Up @@ -207,3 +201,13 @@
border-bottom-left-radius: var(--vkui--size_border_radius--regular);
border-bottom-right-radius: var(--vkui--size_border_radius--regular);
}

/**
* useFocusVisibleClassName()
*/
/* increase specificity for selects */
.FormField--focus-visible.FormField--focus-visible.FormField--focus-visible {
outline: var(--vkui_internal--outline);
outline-width: var(--vkui--size_border--regular);
outline-offset: calc(-1 * var(--vkui--size_border--regular));
}
11 changes: 8 additions & 3 deletions packages/vkui/src/components/FormField/FormField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import * as React from 'react';
import { classNames } from '@vkontakte/vkjs';
import { useAdaptivity } from '../../hooks/useAdaptivity';
import { useExternRef } from '../../hooks/useExternRef';
import { useFocusVisibleClassName } from '../../hooks/useFocusVisibleClassName';
import { useFocusWithin } from '../../hooks/useFocusWithin';
import { SizeType } from '../../lib/adaptivity';
import { HasComponent, HasRootRef } from '../../types';
import { FocusVisible } from '../FocusVisible/FocusVisible';
import styles from './FormField.module.css';

const sizeYClassNames = {
Expand Down Expand Up @@ -65,10 +65,15 @@ export const FormField = ({
...restProps
}: FormFieldOwnProps) => {
const elRef = useExternRef(getRootRef);
const focusWithin = useFocusWithin(elRef);
const { sizeY = 'none' } = useAdaptivity();
const [hover, setHover] = React.useState(false);

const focusWithin = useFocusWithin(elRef);
const focusVisibleClassNames = useFocusVisibleClassName({
focusVisible: focusWithin,
mode: styles['FormField--focus-visible'],
});

const handleMouseEnter = (e: MouseEvent) => {
e.stopPropagation();
setHover(true);
Expand All @@ -92,6 +97,7 @@ export const FormField = ({
sizeY !== SizeType.REGULAR && sizeYClassNames[sizeY],
disabled && styles['FormField--disabled'],
!disabled && hover && styles['FormField--hover'],
focusVisibleClassNames,
className,
)}
>
Expand All @@ -103,7 +109,6 @@ export const FormField = ({
</span>
)}
<span aria-hidden className={styles['FormField__border']} />
<FocusVisible thin visible={focusWithin} mode="outline" />
</Component>
);
};
35 changes: 35 additions & 0 deletions packages/vkui/src/components/Image/Image.e2e-playground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,38 @@ export const ImagePlayground = (props: ComponentPlaygroundProps) => {
</ComponentPlayground>
);
};

export const ImageFocusVisiblePlayground = (props: ComponentPlaygroundProps) => (
<ComponentPlayground
{...props}
propSets={[
{
size: [72],
src: [base64Image],
},
]}
>
{(props: ImageProps) => <Image onClick={() => null} {...props} />}
</ComponentPlayground>
);

export const ImageFocusVisibleOverlayPlayground = (props: ComponentPlaygroundProps) => (
<ComponentPlayground
{...props}
propSets={[
{
size: [72],
src: [base64Image],
children: [
<React.Fragment key="overlay-base">
<Image.Overlay theme="light" visibility="always">
<IconExampleForOverlayBasedOnImageBaseSize />
</Image.Overlay>
</React.Fragment>,
],
},
]}
>
{(props: ImageProps) => <Image {...props} />}
</ComponentPlayground>
);
Loading