adobe / react-spectrum

A collection of libraries and tools that help you build adaptive, accessible, and robust user experiences.
https://react-spectrum.adobe.com
Apache License 2.0
12.52k stars 1.08k forks source link

DatePicker - External label; id prop not applied correctly to group controls #6036

Open SStranks opened 5 months ago

SStranks commented 5 months ago

Provide a general summary of the issue here

When passing in a value to the 'id' prop of DatePicker (and also to an external label 'for' prop), the value is applied to the 'group' sub-component and is not passed on to the controls within. Because the 'group' component is a 'div' and not an input, the external label will not focus (divs are not 'labelable' elements).

Neither the 'DateInput' or 'DateSegment' sub-components accept an id prop, so I can't manually target them; I presume the group sub-component is delegating the ids to its children under the hood somehow, because they do actually end up with ids applied to the generated div spinbuttons.

Passing in an Id from an external label works for the Select and Combobox components; the id gets delegated to the correct sub-component and thus the external label successfully triggers the focus event on that input.

πŸ€” Expected Behavior?

Passing in an Id prop (that is connected to an external label) should result in the user being able to click the external label and the datepicker becomes focused on the first DateSegment input (as happens when using the provided label sub-component).

😯 Current Behavior

No focusing occurs because the id prop is delegated to the 'group' sub-component and HTML Labels can only focus on labelable elements (e.g. button, input, textarea, etc).

πŸ’ Possible Solution

No response

πŸ”¦ Context

No response

πŸ–₯️ Steps to Reproduce

import {Button, Calendar, CalendarCell, CalendarGrid, DateInput, DatePicker, DateSegment, Dialog, Group, Heading, Label, Popover} from 'react-aria-components';

<label htmlFor="someLabel">Click Me!</label>

// This is the example provided at: https://react-spectrum.adobe.com/react-aria/DatePicker.html#example
<DatePicker id="someLabel">
  <Label>Date</Label>
  <Group>
    <DateInput>
      {(segment) => <DateSegment segment={segment} />}
    </DateInput>
    <Button>β–Ό</Button>
  </Group>
  <Popover>
    <Dialog>
      <Calendar>
        <header>
          <Button slot="previous">β—€</Button>
          <Heading />
          <Button slot="next">β–Ά</Button>
        </header>
        <CalendarGrid>
          {(date) => <CalendarCell date={date} />}
        </CalendarGrid>
      </Calendar>
    </Dialog>
  </Popover>
</DatePicker>

Version

"react-aria-components": "^1.1.1"

What browsers are you seeing the problem on?

Chrome

If other, please specify.

No response

What operating system are you using?

WSL2 Ubuntu

🧒 Your Company/Team

No response

πŸ•· Tracking Issue

No response

snowystinger commented 5 months ago

I'm not sure how we'd handle this. The fields are implemented by editable divs, which 'label' won't automatically focus

      <label id="thelabel" htmlFor="someLabel">
        External label
      </label>
      <div id="someLabel" aria-labelledby="thelabel" contentEditable>
        hello
      </div>

I think the expected route would be for you to listen via usePress to the label, then move focus via a ref.

We'd need to decide which ref would make the most sense. I think the DateField ref would make the most sense, in which case we need to useImperativeHandle to expose that method.

In the mean time you could create a focus manager https://react-spectrum.adobe.com/react-aria/FocusScope.html#focusmanager-interface and use focusFirst, that's what we do internally with the label provided via <Label>

SStranks commented 5 months ago

It seems that putting a hidden HTML Input element inside the provided Label component works.

<Label>   
  <input id={props.id} hidden />
</Label>
snowystinger commented 5 months ago

I'm sorry, I'm not following this. Maybe a Stackblitz or Codesandbox would help?