From de7ddf9e4545b1796b2976c927b38b3ab2764a69 Mon Sep 17 00:00:00 2001 From: Dilshod Date: Tue, 28 Feb 2023 18:22:37 +0100 Subject: [PATCH] Enable native label behavior for Switch component (#2265) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add native label behavior for switch * Add reference tests for React and Vue These don’t work in JSDOM so they’re skipped but we can use these to reference expected behavior once we have playwright-based tests * Fix Vue playground switch example * Only prevent default when the element is a label * Port change to Vue * Update changelog --------- Co-authored-by: Jordan Pittman --- packages/@headlessui-react/CHANGELOG.md | 1 + .../src/components/label/label.tsx | 1 + .../src/components/switch/switch.test.tsx | 28 ++++++++++++++- .../src/components/switch/switch.tsx | 6 +++- packages/@headlessui-vue/CHANGELOG.md | 4 ++- .../src/components/label/label.ts | 5 +++ .../src/components/switch/switch.test.tsx | 35 ++++++++++++++++++- .../src/components/switch/switch.ts | 6 +++- .../switch/switch-with-pure-tailwind.tsx | 2 +- .../src/components/switch/switch.vue | 15 +++++--- 10 files changed, 92 insertions(+), 11 deletions(-) diff --git a/packages/@headlessui-react/CHANGELOG.md b/packages/@headlessui-react/CHANGELOG.md index 5b28be2c15..1842c13dcb 100644 --- a/packages/@headlessui-react/CHANGELOG.md +++ b/packages/@headlessui-react/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Ensure `Transition` component completes if nothing is transitioning ([#2318](https://github.com/tailwindlabs/headlessui/pull/2318)) +- Enable native label behavior for `` where possible ([#2265](https://github.com/tailwindlabs/headlessui/pull/2265)) ## [1.7.12] - 2023-02-24 diff --git a/packages/@headlessui-react/src/components/label/label.tsx b/packages/@headlessui-react/src/components/label/label.tsx index 97df90487a..f42131cc3a 100644 --- a/packages/@headlessui-react/src/components/label/label.tsx +++ b/packages/@headlessui-react/src/components/label/label.tsx @@ -99,6 +99,7 @@ function LabelFn( if (passive) { if ('onClick' in ourProps) { + delete (ourProps as any)['htmlFor'] delete (ourProps as any)['onClick'] } diff --git a/packages/@headlessui-react/src/components/switch/switch.test.tsx b/packages/@headlessui-react/src/components/switch/switch.test.tsx index 542edd2e78..eea9f65bb0 100644 --- a/packages/@headlessui-react/src/components/switch/switch.test.tsx +++ b/packages/@headlessui-react/src/components/switch/switch.test.tsx @@ -10,7 +10,7 @@ import { getSwitchLabel, getByText, } from '../../test-utils/accessibility-assertions' -import { press, click, focus, Keys } from '../../test-utils/interactions' +import { press, click, focus, Keys, mouseEnter } from '../../test-utils/interactions' jest.mock('../../hooks/use-id') @@ -595,6 +595,32 @@ describe('Mouse interactions', () => { // Ensure state is still off assertSwitch({ state: SwitchState.Off }) }) + + xit('should be possible to hover the label and trigger a hover on the switch', async () => { + // This test doen't work in JSDOM :( + // Keeping it here for reference when we can test this in a real browser + function Example() { + let [state] = useState(false) + return ( + + + + The label + + ) + } + + render() + + // Verify the switch is not hovered + expect(window.getComputedStyle(getSwitch()!).backgroundColor).toBe('rgb(0, 255, 0)') + + // Hover over the *label* + await mouseEnter(getSwitchLabel()) + + // Make sure the switch gets hover styles + expect(window.getComputedStyle(getSwitch()!).backgroundColor).toBe('rgb(255, 0, 0)') + }) }) describe('Form compatibility', () => { diff --git a/packages/@headlessui-react/src/components/switch/switch.tsx b/packages/@headlessui-react/src/components/switch/switch.tsx index 031a29cce2..ddf7fe927c 100644 --- a/packages/@headlessui-react/src/components/switch/switch.tsx +++ b/packages/@headlessui-react/src/components/switch/switch.tsx @@ -65,8 +65,12 @@ function GroupFn( ) { if (!switchElement) return + if (event.currentTarget.tagName === 'LABEL') { + event.preventDefault() + } switchElement.click() switchElement.focus({ preventScroll: true }) }, diff --git a/packages/@headlessui-vue/CHANGELOG.md b/packages/@headlessui-vue/CHANGELOG.md index 294b03706a..528ba542ee 100644 --- a/packages/@headlessui-vue/CHANGELOG.md +++ b/packages/@headlessui-vue/CHANGELOG.md @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -- Nothing yet! +### Fixed + +- Enable native label behavior for `` where possible ([#2265](https://github.com/tailwindlabs/headlessui/pull/2265)) ## [1.7.11] - 2023-02-24 diff --git a/packages/@headlessui-vue/src/components/label/label.ts b/packages/@headlessui-vue/src/components/label/label.ts index ad5372c9c9..f9502c755a 100644 --- a/packages/@headlessui-vue/src/components/label/label.ts +++ b/packages/@headlessui-vue/src/components/label/label.ts @@ -90,6 +90,11 @@ export let Label = defineComponent({ // @ts-expect-error props are dynamic via context, some components will provide an onClick // then we can delete it. delete ourProps['onClick'] + + // @ts-expect-error props are dynamic via context, some components will provide an htmlFor + // then we can delete it. + delete ourProps['htmlFor'] + // @ts-expect-error props are dynamic via context, some components will provide an onClick // then we can delete it. delete theirProps['onClick'] diff --git a/packages/@headlessui-vue/src/components/switch/switch.test.tsx b/packages/@headlessui-vue/src/components/switch/switch.test.tsx index 462fe0e951..bdac5fb0e9 100644 --- a/packages/@headlessui-vue/src/components/switch/switch.test.tsx +++ b/packages/@headlessui-vue/src/components/switch/switch.test.tsx @@ -10,7 +10,7 @@ import { getSwitchLabel, getByText, } from '../../test-utils/accessibility-assertions' -import { press, click, Keys } from '../../test-utils/interactions' +import { press, click, Keys, mouseEnter } from '../../test-utils/interactions' import { html } from '../../test-utils/html' import { suppressConsoleLogs } from '../../test-utils/suppress-console-logs' @@ -712,6 +712,39 @@ describe('Mouse interactions', () => { // Ensure state is still Off assertSwitch({ state: SwitchState.Off }) }) + + xit('should be possible to hover the label and trigger a hover on the switch', async () => { + // This test doen't work in JSDOM :( + // Keeping it here for reference when we can test this in a real browser + renderTemplate({ + template: html` + + + + The label + + `, + setup() { + return { checked: ref(false) } + }, + }) + + // Verify the switch is not hovered + expect(window.getComputedStyle(getSwitch()!).backgroundColor).toBe('rgb(0, 255, 0)') + + // Hover over the *label* + await mouseEnter(getSwitchLabel()) + + // Make sure the switch gets hover styles + expect(window.getComputedStyle(getSwitch()!).backgroundColor).toBe('rgb(255, 0, 0)') + }) }) describe('Form compatibility', () => { diff --git a/packages/@headlessui-vue/src/components/switch/switch.ts b/packages/@headlessui-vue/src/components/switch/switch.ts index 7d9a3589eb..b2416d1897 100644 --- a/packages/@headlessui-vue/src/components/switch/switch.ts +++ b/packages/@headlessui-vue/src/components/switch/switch.ts @@ -46,8 +46,12 @@ export let SwitchGroup = defineComponent({ let labelledby = useLabels({ name: 'SwitchLabel', props: { - onClick() { + htmlFor: computed(() => switchRef.value?.id), + onClick(event: MouseEvent & { currentTarget: HTMLElement }) { if (!switchRef.value) return + if (event.currentTarget.tagName === 'LABEL') { + event.preventDefault() + } switchRef.value.click() switchRef.value.focus({ preventScroll: true }) }, diff --git a/packages/playground-react/pages/switch/switch-with-pure-tailwind.tsx b/packages/playground-react/pages/switch/switch-with-pure-tailwind.tsx index c392886717..4cd8f4b1b6 100644 --- a/packages/playground-react/pages/switch/switch-with-pure-tailwind.tsx +++ b/packages/playground-react/pages/switch/switch-with-pure-tailwind.tsx @@ -18,7 +18,7 @@ export default function Home() { className={({ checked }) => classNames( 'focus:shadow-outline relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none', - checked ? 'bg-indigo-600' : 'bg-gray-200' + checked ? 'bg-indigo-600 hover:bg-indigo-800' : 'bg-gray-200 hover:bg-gray-400' ) } > diff --git a/packages/playground-vue/src/components/switch/switch.vue b/packages/playground-vue/src/components/switch/switch.vue index 93bdcb91f8..8338333fc7 100644 --- a/packages/playground-vue/src/components/switch/switch.vue +++ b/packages/playground-vue/src/components/switch/switch.vue @@ -3,7 +3,12 @@ Enable notifications - +