Skip to content
Closed
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
7 changes: 7 additions & 0 deletions .changeset/dropdown-menu-group-block-radio-title.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@astryxdesign/cli': patch
---

[docs] The "DropdownMenuGroup — Selectable rows" block titles its radio set with the set's own `title` instead of wrapping it in a `DropdownMenuGroup`, so the menu exposes one group named "Model" rather than two nested groups with the same name.

@vjeux
7 changes: 7 additions & 0 deletions .changeset/dropdown-menu-radio-group-title.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@astryxdesign/core': patch
---

[feat] `DropdownMenuRadioGroup` (also exported as `ContextMenuRadioGroup`) takes a `title`: a visible heading above its radios, drawn like a `DropdownMenuGroup` heading, that names the group. With a title, `label` is optional, and the title names the group so the name always holds the visible heading: a caller's `aria-labelledby` is read after it, and `label` or a caller's `aria-label` is ignored, with a development warning when it differs from the title's text. A `title` that renders no text counts as no title. Give a menu section that holds one radio set this title instead of wrapping the set in a `DropdownMenuGroup`: the menu then exposes one group named by its heading rather than two nested groups with the same name. Without `title` nothing changes.

@vjeux
34 changes: 34 additions & 0 deletions apps/storybook/stories/DropdownMenu.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,40 @@ export const LabRadioGroup: Story = {
},
};

/**
* A section of a menu that holds one radio set gives the set a `title`
* instead of wrapping it in a DropdownMenuGroup: the heading shows as any
* group heading does, and the menu exposes the set as one group named by
* that heading.
*/
export const RadioGroupWithTitle: Story = {
name: 'Radio group with a title',
render: function RadioGroupWithTitleStory() {
const [hidden, setHidden] = useState('hide');
const [showDrafts, setShowDrafts] = useState(true);
return (
<div data-testid="radio-title-fixture">
<DropdownMenu button={{label: 'Filter'}}>
<DropdownMenuCheckboxItem
label="Show drafts"
value={showDrafts}
onChange={setShowDrafts}
/>
<DropdownMenuDivider />
<DropdownMenuRadioGroup
value={hidden}
onChange={setHidden}
title="Hidden files">
<DropdownMenuRadioItem value="hide" label="Hide hidden files" />
<DropdownMenuRadioItem value="show" label="Show hidden files" />
</DropdownMenuRadioGroup>
</DropdownMenu>
<output data-testid="hidden">{hidden}</output>
</div>
);
},
};

export const LabSelectableSizes: Story = {
render: function LabSelectableSizesStory() {
const [sm, setSm] = useState('a');
Expand Down
2 changes: 2 additions & 0 deletions apps/storybook/stories/RadioGroupA11y.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ export const SegmentedOptionGroupDisabledWithMessage = storyFor(

export const MenuGroupSelected = storyFor('menu-group-selected');
export const MenuGroupNoneSelected = storyFor('menu-group-none-selected');
export const MenuGroupTitled = storyFor('menu-group-titled');
export const MenuGroupTitledLabelled = storyFor('menu-group-titled-labelled');
export const MenuOptionUnselectedDescribed = storyFor(
'menu-option-unselected-described',
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const doc = {
name: 'DropdownMenuGroup — Selectable rows',
displayName: 'DropdownMenuGroup — Selectable rows',
description:
'Titled groups around a radio group and checkbox rows — the rows the data API cannot express, which is why the heading lives in compound mode.',
'A radio set titled by its own title and a titled group of checkbox rows — the rows the data API cannot express, which is why the headings live in compound mode. A section holding one radio set titles the set itself rather than a group around it, so it is one group with one name.',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This wording ("radio set", twice) makes astryx search "icon set" match this template too: 367 matches instead of 366, with this template the only new result. packages/cli/test/__golden__/no-icon-set/search-icon-set.json still records 366, so experimental-isolation.test.mjs fails its two "search icon set" cases (CI test-node (1/4)). Running vitest run packages/cli/test/experimental-isolation.test.mjs --update on the branch and committing the golden, after checking its diff changes only the count, fixes it.

isReady: true,
aspectRatio: 16 / 9,
componentsUsed: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,15 @@ export default function DropdownMenuGroupWithSelectable() {

return (
<DropdownMenu button={{label: 'Run settings'}}>
<DropdownMenuGroup title="Model">
<DropdownMenuRadioGroup
label="Model"
value={model}
onChange={setModel}
hasCloseOnSelect={false}>
<DropdownMenuRadioItem value="fast" label="Fast" />
<DropdownMenuRadioItem value="balanced" label="Balanced" />
<DropdownMenuRadioItem value="thorough" label="Thorough" />
</DropdownMenuRadioGroup>
</DropdownMenuGroup>
<DropdownMenuRadioGroup
title="Model"
value={model}
onChange={setModel}
hasCloseOnSelect={false}>
<DropdownMenuRadioItem value="fast" label="Fast" />
<DropdownMenuRadioItem value="balanced" label="Balanced" />
<DropdownMenuRadioItem value="thorough" label="Thorough" />
</DropdownMenuRadioGroup>
<DropdownMenuDivider />
<DropdownMenuGroup title="On completion">
<DropdownMenuCheckboxItem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"type": "search",
"data": {
"query": "icon set",
"matchCount": 370,
"matchCount": 371,
"results": [
{
"domain": "doc",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Results for "icon set" (20 of 370)
Results for "icon set" (20 of 371)
name: authoring/building-blocks/templates/build-the-template/template-assets/template-icons
section: use-astryx-icon
domain: doc
Expand Down
18 changes: 14 additions & 4 deletions packages/core/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,13 @@ export const docs = {
name: 'label',
type: 'string',
description:
'Accessible name for the group, applied as aria-label so screen readers announce the radios as a named set, e.g. "Sort by". Required even when aria-labelledby (via base props) points to visible text; that visible reference becomes the computed name while label remains the fallback.',
required: true,
'Accessible name for the group, applied as aria-label so screen readers announce the radios as a named set, e.g. "Sort by". Required unless title is given. An aria-labelledby passed through base props that points at visible text becomes the computed name, while label remains the fallback. With a title, the title names the group instead, so the name holds the visible heading; label (or an aria-label passed through base props) is then ignored, with a development warning when it differs from the text of the title. Without a title, an aria-label passed through base props names the group when label is omitted.',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This description now makes label required only without a title, but the best practice at line 24 of this file still tells builders "label remains required and supplies the aria-label fallback", and the usage text (line 14) and anatomy (lines 42–55) leave the title out. Rewriting line 24 to the title-or-label rule and adding a Title anatomy part, as DropdownMenuGroup.doc.mjs has, keeps astryx docs consistent with the component.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This now says label is required only without a title, but the best practice at line 24 still reads "label remains required and supplies the aria-label fallback", and beside a title the component drops label from the markup. Please rewrite that best practice to the title-or-label rule, mention title in the usage text (line 14), and add a Title part to the anatomy (lines 42–55).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes label required only without a title, but the best practice at line 24 of this file still tells builders "label remains required and supplies the aria-label fallback", and the usage text (line 14) and anatomy (lines 42–55) leave the title out. Please rewrite line 24 to the title-or-label rule, mention title in the usage text, and add a Title anatomy part, as DropdownMenuGroup.doc.mjs has for its heading.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This now says label is required only without a title and is ignored beside one, but the best practice at line 24 of this file still says "label remains required and supplies the aria-label fallback". Please rewrite line 24 to the title-or-label rule, mention title in the usage text (line 14), and add a Title part to the anatomy.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This now says label is required only without a title, but the best practice at line 24 of this file still says "label remains required", the usage text (line 14) asks for a label, and the anatomy (lines 42–55) has no title part. Update those three so astryx docs gives one rule.

},
{
name: 'title',
type: 'ReactNode',
description:
'A visible heading above the radios, with the same treatment as a DropdownMenuGroup heading, that names the group through aria-labelledby; an aria-labelledby passed through base props is read after it. With a title, label is optional. A title that renders no text (true, an empty fragment, a component that returns null) counts as no title: no heading shows and the group is named as without one. Give a menu section that holds one radio set this title instead of wrapping the set in a DropdownMenuGroup, so the set is one group with one name rather than two nested groups with the same name.',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not blocking: DropdownMenuGroup.doc.mjs tells builders "Keep title content non-interactive and place controls in DropdownMenu header or footer", because a control in the heading is outside the arrow-key order, and proposed FR7 and DEC-1 say the same holds here. Adding that sentence to this description (and to the Chinese, dense and JSDoc copies) keeps builders from putting a control keyboard users cannot reach into a radio set's title.

},
{
name: 'hasCloseOnSelect',
Expand Down Expand Up @@ -132,7 +137,9 @@ export const docsZh = {
value: '组中当前选中的值。尚无选中项时传 undefined。',
onChange: '所选值变化时触发的回调。',
label:
'组的无障碍名称,作为 aria-label 应用。即使 aria-labelledby(通过基础属性)指向可见文本,label 仍然必需并作为后备名称。',
'组的无障碍名称,作为 aria-label 应用,以便屏幕阅读器将单选项作为命名集合朗读,例如“Sort by”。未提供 title 时必需。通过基础属性传入、指向可见文本的 aria-labelledby 会成为计算出的名称,label 作为后备名称。提供 title 时改由 title 命名该组,使名称始终包含可见标题;此时 label(或通过基础属性传入的 aria-label)会被忽略,若其与 title 的文本不同,开发环境会发出警告。未提供 title 且省略 label 时,通过基础属性传入的 aria-label 为该组命名。',
title:
'显示在单选项上方的可见标题,样式与 DropdownMenuGroup 的标题相同,并通过 aria-labelledby 为该组命名;通过基础属性传入的 aria-labelledby 会接在其后。提供 title 时 label 可省略。不渲染任何文本的 title(true、空 Fragment、返回 null 的组件)视为未提供:不显示标题,组按未提供 title 时的方式命名。菜单中只包含一组单选项的分区应使用此标题,而不是用 DropdownMenuGroup 包裹该组,这样该组只是一个具有一个名称的组。',
hasCloseOnSelect: '选择某值是否关闭菜单。默认关闭。',
ref: '转发到 role="group" 根元素的 ref。',
children: '组成该组的 DropdownMenuRadioItem。',
Expand All @@ -147,7 +154,10 @@ export const docsDense = {
propDescriptions: {
value: 'selected value (undefined = none)',
onChange: 'fired when selected value changes',
label: 'accessible group name (applied as aria-label); required',
label:
'accessible group name (applied as aria-label); required unless title is given',
title:
'visible heading (DropdownMenuGroup treatment) that names the group via aria-labelledby; use it instead of a DropdownMenuGroup around one radio set',
hasCloseOnSelect: 'close menu on select (default true)',
ref: 'forwarded to the role="group" root',
children: 'the DropdownMenuRadioItems',
Expand Down
Loading
Loading