Skip to content
Merged
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
63 changes: 63 additions & 0 deletions apps/web/app/tests/test-user-select-none.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Test script to verify user-select: none is working on interactive elements

import { test, expect } from '@playwright/test';

// This test would verify that text selection is disabled on interactive elements
test('Interactive elements should not allow text selection', async ({ page }) => {
// Navigate to the app
await page.goto('http://localhost:3000');

// Test buttons - try to select text within them
const buttons = await page.locator('button').all();

for (const button of buttons) {
const isVisible = await button.isVisible();
if (isVisible) {
// Try to select all text in the button
await button.selectText().catch(() => {
// Expected to fail or select nothing due to select-none
});

// Check if any text was actually selected
const selection = await page.evaluate(() => window.getSelection().toString());

// If the button contains only non-selectable elements, selection should be empty
// This test would pass if our select-none implementation is working
console.log(`Button selection result: "${selection}"`);
}
}

// Test badges, toggles, tabs, etc.
const interactiveElements = [
'[data-testid="badge"]',
'[role="tab"]',
'[role="switch"]',
'[role="checkbox"]',
'[role="radio"]',
];

for (const selector of interactiveElements) {
const elements = await page.locator(selector).all();

for (const element of elements) {
const isVisible = await element.isVisible();
if (isVisible) {
await element.selectText().catch(() => {
// Expected to fail due to select-none
});

const selection = await page.evaluate(() => window.getSelection().toString());
console.log(`${selector} selection result: "${selection}"`);
}
}
}
});

// Simple manual test instructions
console.log(`
Manual Test Instructions:
1. Open http://localhost:3000 in your browser
2. Try to select text within buttons, badges, tabs, toggles, checkboxes, etc.
3. The text within these interactive elements should not be selectable
4. You should see the cursor change but no text highlighting should occur
`);
2 changes: 1 addition & 1 deletion packages/ui/src/components/accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const AccordionTrigger = React.forwardRef<
<AccordionPrimitive.Header className='flex'>
<AccordionPrimitive.Trigger
className={cn(
'flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all [&[data-state=open]>svg]:rotate-180',
'flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all [&[data-state=open]>svg]:rotate-180 select-none',
className,
)}
ref={ref}
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/components/badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type * as React from 'react';
import { cn } from '../lib/utils';

const badgeVariants = cva(
'inline-flex items-center gap-1.5 whitespace-nowrap rounded-xs px-2 py-0.5 font-medium text-[0.7rem] transition-all duration-200',
'inline-flex items-center gap-1.5 whitespace-nowrap rounded-xs px-2 py-0.5 font-medium text-[0.7rem] transition-all duration-200 select-none',
{
variants: {
variant: {
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/components/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { cn } from '../lib/utils';
import { Tooltip } from './tooltip';

const buttonVariants = cva(
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium text-sm transition-colors duration-200 focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring/30 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 btn-hover-effect',
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium text-sm transition-colors duration-200 focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring/30 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 btn-hover-effect select-none',
{
variants: {
variant: {
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/components/checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const Checkbox = React.forwardRef<
>(({ className, ...props }, ref) => (
<CheckboxPrimitive.Root
className={cn(
'border-primary focus-visible:ring-ring-3 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground focus-visible:outline-hidden peer h-5 w-5 shrink-0 rounded-md border focus-visible:ring-1 disabled:cursor-not-allowed disabled:opacity-50',
'border-primary focus-visible:ring-ring-3 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground focus-visible:outline-hidden peer h-5 w-5 shrink-0 rounded-md border focus-visible:ring-1 disabled:cursor-not-allowed disabled:opacity-50 select-none',
className,
)}
ref={ref}
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/components/navigation-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName;
const NavigationMenuItem = NavigationMenuPrimitive.Item;

const navigationMenuTriggerStyle = cva(
'group inline-flex h-9 w-max items-center justify-center rounded-md bg-white px-4 py-2 font-medium text-sm transition-colors hover:bg-stone-100 hover:text-stone-900 focus:bg-stone-100 focus:text-stone-900 focus:outline-hidden disabled:pointer-events-none disabled:opacity-50 data-[state=open]:bg-stone-100/50 data-[state=open]:text-stone-900 data-[state=open]:focus:bg-stone-100 data-[state=open]:hover:bg-stone-100 dark:bg-stone-950 dark:data-[state=open]:bg-stone-800/50 dark:data-[state=open]:text-stone-50 dark:focus:bg-stone-800 dark:focus:text-stone-50 dark:data-[state=open]:focus:bg-stone-800 dark:hover:bg-stone-800 dark:hover:text-stone-50 dark:data-[state=open]:hover:bg-stone-800',
'group inline-flex h-9 w-max items-center justify-center rounded-md bg-white px-4 py-2 font-medium text-sm transition-colors hover:bg-stone-100 hover:text-stone-900 focus:bg-stone-100 focus:text-stone-900 focus:outline-hidden disabled:pointer-events-none disabled:opacity-50 data-[state=open]:bg-stone-100/50 data-[state=open]:text-stone-900 data-[state=open]:focus:bg-stone-100 data-[state=open]:hover:bg-stone-100 dark:bg-stone-950 dark:data-[state=open]:bg-stone-800/50 dark:data-[state=open]:text-stone-50 dark:focus:bg-stone-800 dark:focus:text-stone-50 dark:data-[state=open]:focus:bg-stone-800 dark:hover:bg-stone-800 dark:hover:text-stone-50 dark:data-[state=open]:hover:bg-stone-800 select-none',
);

const NavigationMenuTrigger = React.forwardRef<
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/components/radio-group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const RadioGroupItem = React.forwardRef<
return (
<RadioGroupPrimitive.Item
className={cn(
'text-primary focus-visible:ring-ring-3 focus:outline-hidden aspect-square size-5 rounded-full border-2 focus-visible:ring-2 disabled:cursor-not-allowed disabled:opacity-50',
'text-primary focus-visible:ring-ring-3 focus:outline-hidden aspect-square size-5 rounded-full border-2 focus-visible:ring-2 disabled:cursor-not-allowed disabled:opacity-50 select-none',
className,
)}
ref={ref}
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/components/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const SelectTrigger = React.forwardRef<
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Trigger
className={cn(
'border-input bg-background ring-offset-background placeholder:text-muted-foreground focus:outline-hidden focus:ring-ring-3 flex h-10 w-full items-center justify-between rounded-md border px-3 py-2 text-sm focus:ring-2 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
'border-input bg-background ring-offset-background placeholder:text-muted-foreground focus:outline-hidden focus:ring-ring-3 flex h-10 w-full items-center justify-between rounded-md border px-3 py-2 text-sm focus:ring-2 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1 select-none',
className,
)}
ref={ref}
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/components/slider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ function Slider({
</SliderPrimitive.Track>
{Array.from({ length: _values.length }, (_, index) => (
<SliderPrimitive.Thumb
className='border-primary bg-background ring-ring-3/50 focus-visible:outline-hidden shadow-xs block size-4 shrink-0 rounded-full border transition-[color,box-shadow-xs] hover:ring-4 focus-visible:ring-4 disabled:pointer-events-none disabled:opacity-50'
className='border-primary bg-background ring-ring-3/50 focus-visible:outline-hidden shadow-xs block size-4 shrink-0 rounded-full border transition-[color,box-shadow-xs] hover:ring-4 focus-visible:ring-4 disabled:pointer-events-none disabled:opacity-50 select-none'
data-slot='slider-thumb'
key={index}
/>
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/components/switch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const Switch = React.forwardRef<
>(({ className, ...props }, ref) => (
<SwitchPrimitive.Root
className={cn(
'focus-visible:ring-ring-3 focus-visible:ring-offset-background data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:outline-hidden peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
'focus-visible:ring-ring-3 focus-visible:ring-offset-background data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:outline-hidden peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 select-none',
className,
)}
{...props}
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/components/tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const TabsTrigger = React.forwardRef<
>(({ className, ...props }, ref) => (
<TabsPrimitive.Trigger
className={cn(
'ring-offset-background focus-visible:ring-ring-3 data-[state=active]:border-foreground data-[state=active]:text-brand focus-visible:outline-hidden inline-flex items-center justify-center gap-2 whitespace-nowrap px-3 py-2 text-sm font-medium transition-all focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:border-b-2',
'ring-offset-background focus-visible:ring-ring-3 data-[state=active]:border-foreground data-[state=active]:text-brand focus-visible:outline-hidden inline-flex items-center justify-center gap-2 whitespace-nowrap px-3 py-2 text-sm font-medium transition-all focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:border-b-2 select-none',
className,
)}
ref={ref}
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/components/toggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as React from 'react';
import { cn } from '../lib/utils';

const toggleVariants = cva(
'inline-flex items-center justify-center gap-2 rounded-md font-medium text-sm transition-all duration-200 hover:bg-stone-100/70 hover:text-stone-500 focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-stone-950 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-stone-100/90 data-[state=on]:text-stone-900 dark:data-[state=on]:bg-stone-800/90 dark:data-[state=on]:text-stone-50 dark:focus-visible:ring-stone-300 dark:hover:bg-stone-800/70 dark:hover:text-stone-400 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 toggle-hover-effect',
'inline-flex items-center justify-center gap-2 rounded-md font-medium text-sm transition-all duration-200 hover:bg-stone-100/70 hover:text-stone-500 focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-stone-950 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-stone-100/90 data-[state=on]:text-stone-900 dark:data-[state=on]:bg-stone-800/90 dark:data-[state=on]:text-stone-50 dark:focus-visible:ring-stone-300 dark:hover:bg-stone-800/70 dark:hover:text-stone-400 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 toggle-hover-effect select-none',
{
variants: {
variant: {
Expand Down