diff --git a/.github/instructions/instruction-architecture.instructions.md b/.github/instructions/instruction-architecture.instructions.md index 5a7289852607..ee7d1cd6c981 100644 --- a/.github/instructions/instruction-architecture.instructions.md +++ b/.github/instructions/instruction-architecture.instructions.md @@ -4,10 +4,36 @@ applyTo: ".github/instructions/**,.github/agents/**" # Editing Copilot content instruction and agent files -This applies when you add, edit, or remove a Copilot instruction or shared agent file that guides how **content** (articles, data files) is written. It does **not** apply to code instructions or agents owned by the engineering team (for example `code.instructions.md`). +This applies when you add, edit, review, or remove a Copilot instruction or shared agent file that guides how **content** (articles, data files) is written. It does **not** apply to code instructions or agents owned by the engineering team (for example `code.instructions.md`). -When it applies, you **must** first read the instruction-architecture doc in full and follow it: +Content-writing guidance goes in one of two places. Decide which before writing anything. -https://github.com/github/technical-content/blob/main/contributing-to-docs/docs-work/copilot-instruction-architecture.md +## Always-on instruction files (`.github/instructions/*.instructions.md`) -Read the current version every time (from a local `github/technical-content` checkout if you have one, otherwise fetch the URL); do not rely on your memory of it, because it changes. If you cannot access it, say so and stop rather than guessing. +These load automatically into every Copilot interaction whose files match the `applyTo` pattern. Use them only for rules that apply to every content interaction, regardless of persona, task, or phase. + +* Keep the always-on set that loads for content under roughly **150 discrete rules**, with a soft backstop of **~6,500 tokens**. Rule count matters more, because adherence slips as the number of instructions grows. Both are soft warnings: slightly over is fine. Run `npm run measure-instruction-budget` to see where a change lands, and report the before and after numbers. +* Front-load the most important rules. Rules earlier in a file are followed more reliably, and the order in which separate files are combined is not guaranteed. +* A rule earns a spot only if it scores well on these factors: + * **Model default gap**: The model consistently gets it wrong without the rule. + * **Breadth**: It applies to every content interaction, not just some personas, tasks, or phases. + * **Cost of missing it**: Getting it wrong causes expensive rework (wrong audience, wrong scope), not polish that is cheap to catch in review. + * **Conciseness**: It fits in one or two short, actionable rules. +* Write rules as short imperatives, not multi-paragraph explanations. + +## Shared agent files (`.github/agents/`) + +Writers invoke these deliberately. Each agent has its own budget (up to 30,000 characters), so guidance that is narrow, detailed, or needs room to explain goes here. There are three kinds: + +* **Persona agents** target a specific audience: `builder-writer` (developers building software) and `driver-writer` (admins and decision-makers enabling developers at scale). +* **Task agents** help with a specific type of work, such as a content design plan or release notes. +* **Review agents** give a focused editing pass on existing content, such as readability. + +Use agents, not skills, for docs-writing guidance. Skills can be invoked automatically and unpredictably, which undermines consistent output across the team. + +## Always + +* Do not duplicate guidance that already exists in another instruction file or shared agent. Check before adding. +* Keep content-writing guidance in these shared files, never in personal instructions, so it stays consistent across the team. + +The reasoning behind these rules is written up for humans at https://github.com/github/technical-content/blob/main/contributing-to-docs/docs-work/copilot-instruction-architecture.md. You do not need to read it to follow them. diff --git a/.github/workflows/reviewers-docs-engineering.yml b/.github/workflows/reviewers-docs-engineering.yml index cb5e3527ea6f..9b3b92a6e780 100644 --- a/.github/workflows/reviewers-docs-engineering.yml +++ b/.github/workflows/reviewers-docs-engineering.yml @@ -1,10 +1,7 @@ -name: Reviewers - Docs Engineering +name: Add engineering PRs to review board -# **What it does**: Automatically add reviewers based on paths, but only for the docs-internal repo. -# And sets the 'engineering' label on the PR. It also edits the PR body to add a template -# for asking questions for the sake of being confident about the PRs rollout. -# **Why we have it**: So we can have reviewers automatically without getting open source notifications. -# **Who does it impact**: Docs team. +# Routes non-draft engineering PRs to the Docs & Blog Engineering PR Reviews board for triage. +# Labels each PR, so later pushes skip board work unless the PR reopens. on: pull_request: @@ -19,9 +16,9 @@ on: - '**.tsx' - '**.scss' - 'src/**' - - '!src/**.json' # So that Docs Engineering isn't reviewing automated pipeline data PRs - - '!src/**.yml' # So that Docs Engineering isn't reviewing automated pipeline data PRs - - '!src/**.sha' # So that Docs Engineering isn't reviewing automated pipeline data PRs + - '!src/**.json' # Docs Engineering does not triage automated pipeline data PRs. + - '!src/**.yml' # Docs Engineering does not triage automated pipeline data PRs. + - '!src/**.sha' # Docs Engineering does not triage automated pipeline data PRs. - '.github/**' - 'config/**' - '.devcontainer/**' @@ -39,7 +36,8 @@ jobs: if: >- ${{ github.repository == 'github/docs-internal' && !github.event.pull_request.draft && - !contains(github.event.pull_request.labels.*.name, 'reviewers-docs-engineering') && + (github.event.action == 'reopened' || + !contains(github.event.pull_request.labels.*.name, 'reviewers-docs-engineering')) && !contains(github.event.pull_request.labels.*.name, 'lockfile-churn-only') && github.event.pull_request.head.ref != 'repo-sync' }} runs-on: ubuntu-latest @@ -52,13 +50,13 @@ jobs: # Detect PRs that only changed package-lock.json (no engineering source files). # These are usually cross-platform `npm install` churn from contributors - # editing content. We comment with reset instructions instead of pulling in - # docs-engineering for review. + # editing content. We comment with reset instructions instead of adding + # them to the engineering review board. # # Dependabot is exempt. Its security updates for transitive dependencies # change only the lockfile, because the dependency is not in package.json. # Those PRs are intentional, so the reset instructions are wrong and - # suppressing the review request leaves them with no reviewer at all. + # keeping them off the board leaves them without engineering triage. - name: Detect lockfile-only churn id: detect env: @@ -97,15 +95,19 @@ jobs: git push ``` - If the lockfile change is intentional, remove the `lockfile-churn-only` label and request docs-engineering review. + If the lockfile change is intentional, remove the `lockfile-churn-only` label and push or reopen the PR so this workflow sends it to the engineering review board. EOF gh pr comment "$PR" --body-file /tmp/lockfile-churn-body.md gh pr edit "$PR" --add-label lockfile-churn-only - - name: Add docs engineering as a reviewer + - name: Add PR to board and label if: steps.detect.outputs.lockfile_only != 'true' env: GH_TOKEN: ${{ secrets.DOCS_BOT_PAT_BASE }} + REPO: ${{ github.repository }} + PR_NUMBER: ${{ github.event.pull_request.number }} uses: ./.github/actions/retry-command with: - command: gh pr edit $PR --add-reviewer github/docs-engineering --add-label reviewers-docs-engineering + command: >- + gh project item-add 25672 --owner github --url "$PR" && + gh api "repos/$REPO/issues/$PR_NUMBER/labels" -f 'labels[]=reviewers-docs-engineering' --silent diff --git a/content/copilot/reference/ai-models/model-hosting.md b/content/copilot/reference/ai-models/model-hosting.md index 4765f740441b..ce71be2d6b5e 100644 --- a/content/copilot/reference/ai-models/model-hosting.md +++ b/content/copilot/reference/ai-models/model-hosting.md @@ -30,6 +30,7 @@ Used for: * {% data variables.copilot.copilot_gpt_6_astra %} * {% data variables.copilot.copilot_gpt_6_luna %} * {% data variables.copilot.copilot_gpt_6_sol %} +* {% data variables.copilot.copilot_gpt_61_sol %} These models are hosted by OpenAI and {% data variables.product.github %}'s Azure infrastructure. diff --git a/content/copilot/reference/ai-models/supported-models.md b/content/copilot/reference/ai-models/supported-models.md index cc4530968d72..fb556cac70c8 100644 --- a/content/copilot/reference/ai-models/supported-models.md +++ b/content/copilot/reference/ai-models/supported-models.md @@ -97,6 +97,7 @@ Choosing a larger context window or higher reasoning will impact {% data variabl | {% data variables.copilot.copilot_gpt_6_astra %} | {% octicon "check" aria-label="Supported" %} | {% octicon "check" aria-label="Supported" %} | | {% data variables.copilot.copilot_gpt_6_luna %} | {% octicon "check" aria-label="Supported" %} | {% octicon "check" aria-label="Supported" %} | | {% data variables.copilot.copilot_gpt_6_sol %} | {% octicon "check" aria-label="Supported" %} | {% octicon "check" aria-label="Supported" %} | +| {% data variables.copilot.copilot_gpt_61_sol %} | {% octicon "check" aria-label="Supported" %} | {% octicon "check" aria-label="Supported" %} | | {% data variables.copilot.copilot_kimi_k3 %} | {% octicon "check" aria-label="Supported" %} | {% octicon "check" aria-label="Supported" %} | {% endrowheaders %} @@ -140,6 +141,7 @@ Some {% data variables.product.prodname_copilot_short %} models require minimum | {% data variables.copilot.copilot_gpt_6_astra %} | `1.136.1` | `17.14.19` | TBD | TBD | TBD | | {% data variables.copilot.copilot_gpt_6_luna %} | TBD | `17.14.19` | TBD | TBD | TBD | | {% data variables.copilot.copilot_gpt_6_sol %} | TBD | `17.14.19` | TBD | TBD | TBD | +| {% data variables.copilot.copilot_gpt_61_sol %} | TBD | `17.14.19` | TBD | TBD | TBD | | {% data variables.copilot.copilot_claude_opus_48 %} | `v1.118` | `17.14.6` | TBD | TBD | TBD | | {% data variables.copilot.copilot_claude_opus_5 %} | `v1.128.0` | `17.14.22` | TBD | TBD | TBD | | {% data variables.copilot.copilot_claude_opus_55 %} | TBD | `17.14.6` | TBD | TBD | TBD | diff --git a/content/copilot/reference/copilot-billing/models-and-pricing.md b/content/copilot/reference/copilot-billing/models-and-pricing.md index c316385ca524..b78eeffce4f1 100644 --- a/content/copilot/reference/copilot-billing/models-and-pricing.md +++ b/content/copilot/reference/copilot-billing/models-and-pricing.md @@ -33,7 +33,7 @@ All prices are **per 1 million tokens**. {% data reusables.copilot.extended-context-pricing %} -{% data variables.copilot.copilot_gpt_56_sol %}, {% data variables.copilot.copilot_gpt_56_terra %}, {% data variables.copilot.copilot_gpt_56_luna %}, {% data variables.copilot.copilot_gpt_6_astra %}, {% data variables.copilot.copilot_gpt_6_luna %}, and {% data variables.copilot.copilot_gpt_6_sol %} include a cache write cost in addition to cached input. Earlier OpenAI models have no cache write cost. +{% data variables.copilot.copilot_gpt_56_sol %}, {% data variables.copilot.copilot_gpt_56_terra %}, {% data variables.copilot.copilot_gpt_56_luna %}, {% data variables.copilot.copilot_gpt_6_astra %}, {% data variables.copilot.copilot_gpt_6_luna %}, {% data variables.copilot.copilot_gpt_6_sol %}, and {% data variables.copilot.copilot_gpt_61_sol %} include a cache write cost in addition to cached input. Earlier OpenAI models have no cache write cost. | Model | Release status | Category | Tier | Threshold (input tokens) | Input | Cached input | Cache write | Output | | --- | --- | --- | --- | --- | ---: | ---: | ---: | ---: | diff --git a/data/reusables/copilot/copilot-cloud-agent-non-auto-models.md b/data/reusables/copilot/copilot-cloud-agent-non-auto-models.md index d4d540cf1cae..03b55680a19e 100644 --- a/data/reusables/copilot/copilot-cloud-agent-non-auto-models.md +++ b/data/reusables/copilot/copilot-cloud-agent-non-auto-models.md @@ -14,6 +14,7 @@ * {% data variables.copilot.copilot_gpt_6_astra %} * {% data variables.copilot.copilot_gpt_6_luna %} * {% data variables.copilot.copilot_gpt_6_sol %} +* {% data variables.copilot.copilot_gpt_61_sol %} * {% data variables.copilot.copilot_grok_45 %} * {% data variables.copilot.copilot_grok_46 %} * {% data variables.copilot.copilot_grok_47 %} diff --git a/data/tables/copilot/model-comparison.yml b/data/tables/copilot/model-comparison.yml index e809ca6b35ac..b4aedd92909f 100644 --- a/data/tables/copilot/model-comparison.yml +++ b/data/tables/copilot/model-comparison.yml @@ -56,11 +56,16 @@ - name: GPT-6 Luna task_area: Fast help with simple or repetitive tasks excels_at: Quick, cost-efficient responses for smaller, faster tasks - further_reading: 'Coming soon' + further_reading: '[GPT-6 Luna model card](https://deploymentsafety.openai.com/gpt-6-astra/gpt-6-astra.pdf)' - name: GPT-6 Sol task_area: Interactive and agentic coding excels_at: All-round development tasks that benefit from careful, multistep validation + further_reading: '[GPT-6 Sol model card](https://deploymentsafety.openai.com/gpt-6-astra/gpt-6-astra.pdf)' + +- name: GPT-6.1 Sol + task_area: Complex coding tasks + excels_at: Advanced, efficient reasoning for complex coding tasks further_reading: 'Coming soon' # Anthropic diff --git a/data/tables/copilot/model-release-status.yml b/data/tables/copilot/model-release-status.yml index 2cee9f7eab8d..7dc65b595cae 100644 --- a/data/tables/copilot/model-release-status.yml +++ b/data/tables/copilot/model-release-status.yml @@ -63,6 +63,10 @@ provider: 'OpenAI' release_status: 'GA' +- name: 'GPT-6.1 Sol' + provider: 'OpenAI' + release_status: 'GA' + # Anthropic models - name: 'Claude Fable 5' diff --git a/data/tables/copilot/model-supported-plans.yml b/data/tables/copilot/model-supported-plans.yml index 1452069b07a9..ef37d4c026dc 100644 --- a/data/tables/copilot/model-supported-plans.yml +++ b/data/tables/copilot/model-supported-plans.yml @@ -201,6 +201,13 @@ business: true enterprise: true +- name: GPT-6.1 Sol + pro: false + pro_plus: true + max: true + business: true + enterprise: true + - name: Grok 4.5 pro: true pro_plus: true diff --git a/data/tables/copilot/models-and-pricing.yml b/data/tables/copilot/models-and-pricing.yml index 795f366a8493..8cddd0a838c0 100644 --- a/data/tables/copilot/models-and-pricing.yml +++ b/data/tables/copilot/models-and-pricing.yml @@ -240,6 +240,28 @@ output: $15.00 cache_write: $5.00 +- model: GPT-6.1 Sol + provider: openai + release_status: GA + category: Powerful + threshold: '≤ 272K' + tier: Default + input: $2.00 + cached_input: $0.10 + output: $10.00 + cache_write: $2.50 + +- model: GPT-6.1 Sol + provider: openai + release_status: GA + category: Powerful + threshold: '> 272K' + tier: 'Long context' + input: $4.00 + cached_input: $0.20 + output: $15.00 + cache_write: $5.00 + # Anthropic - model: Claude Haiku 4.5 provider: anthropic diff --git a/data/variables/copilot.yml b/data/variables/copilot.yml index 42d046597d72..e1bee4ee6b0e 100644 --- a/data/variables/copilot.yml +++ b/data/variables/copilot.yml @@ -237,6 +237,7 @@ copilot_gpt_56_terra: 'GPT-5.6 Terra' copilot_gpt_6_astra: 'GPT-6 Astra' copilot_gpt_6_luna: 'GPT-6 Luna' copilot_gpt_6_sol: 'GPT-6 Sol' +copilot_gpt_61_sol: 'GPT-6.1 Sol' # OpenAI 'o' series: copilot_o3: 'o3' copilot_o4_mini: 'o4-mini' diff --git a/src/fixtures/fixtures/content/get-started/cookbook/examples/debugging.md b/src/fixtures/fixtures/content/get-started/cookbook/examples/debugging.md new file mode 100644 index 000000000000..5bfc0f206cc6 --- /dev/null +++ b/src/fixtures/fixtures/content/get-started/cookbook/examples/debugging.md @@ -0,0 +1,14 @@ +--- +title: Debug a workflow +intro: Diagnose a failed workflow. +versions: + fpt: '*' +category: + - Debugging code +surface: + - CLI +complexity: + - Advanced +--- + +Diagnose a failed workflow. diff --git a/src/fixtures/fixtures/content/get-started/cookbook/examples/index.md b/src/fixtures/fixtures/content/get-started/cookbook/examples/index.md new file mode 100644 index 000000000000..90e049c8d0dc --- /dev/null +++ b/src/fixtures/fixtures/content/get-started/cookbook/examples/index.md @@ -0,0 +1,8 @@ +--- +title: Cookbook examples +versions: + fpt: '*' +children: + - /testing + - /debugging +--- diff --git a/src/fixtures/fixtures/content/get-started/cookbook/examples/testing.md b/src/fixtures/fixtures/content/get-started/cookbook/examples/testing.md new file mode 100644 index 000000000000..8c9ecc542a83 --- /dev/null +++ b/src/fixtures/fixtures/content/get-started/cookbook/examples/testing.md @@ -0,0 +1,14 @@ +--- +title: Write unit tests +intro: Generate tests for a function. +versions: + fpt: '*' +category: + - Improve quality and maintainability +surface: + - Chat +complexity: + - Simple +--- + +Generate tests for a function. diff --git a/src/fixtures/fixtures/content/get-started/cookbook/index.md b/src/fixtures/fixtures/content/get-started/cookbook/index.md new file mode 100644 index 000000000000..387815b69fd4 --- /dev/null +++ b/src/fixtures/fixtures/content/get-started/cookbook/index.md @@ -0,0 +1,16 @@ +--- +title: Cookbook filters +intro: Test search and category landing filters. +versions: + fpt: '*' +layout: category-landing +filters: + - category + - surface + - complexity +spotlight: + - article: /examples/testing + image: /assets/images/placeholder.png +children: + - /examples +--- diff --git a/src/fixtures/fixtures/content/get-started/index.md b/src/fixtures/fixtures/content/get-started/index.md index 9d6cf97acd56..63568b0eb672 100644 --- a/src/fixtures/fixtures/content/get-started/index.md +++ b/src/fixtures/fixtures/content/get-started/index.md @@ -48,6 +48,7 @@ children: - /multi-carousel - /non-child-resolution - /discovery-filtered + - /cookbook communityRedirect: name: Provide HubGit Feedback href: 'https://hubgit.com/orgs/community/discussions/categories/get-started' diff --git a/src/fixtures/tests/playwright-rendering.spec.ts b/src/fixtures/tests/playwright-rendering.spec.ts index fa0e20479ab3..d59a4d672228 100644 --- a/src/fixtures/tests/playwright-rendering.spec.ts +++ b/src/fixtures/tests/playwright-rendering.spec.ts @@ -1634,6 +1634,86 @@ test.describe('Docs 2026 in-article navigation', () => { }) }) +test.describe('CookBookFilter component', () => { + const filters = [ + { name: 'Category', value: 'Improve quality and maintainability' }, + { name: 'Surface', value: 'Chat' }, + { name: 'Complexity', value: 'Simple' }, + ] + + for (const { name, value } of filters) { + test(`${name} filters articles`, async ({ page }) => { + await page.goto('/get-started/cookbook') + await expect(page.getByRole('heading', { name: 'Explore 2 examples' })).toBeVisible() + + const trigger = page.getByRole('button', { name: new RegExp(`^${name}:`) }) + await trigger.click() + const menu = page.getByRole('menu', { name, exact: true }) + await menu.getByRole('menuitemradio', { name: value, exact: true }).click() + + await expect(page.getByRole('heading', { name: 'Explore 1 examples' })).toBeVisible() + await expect(trigger).toHaveText(`${name}: ${value}`) + await trigger.click() + await expect(menu.getByRole('menuitemradio', { checked: true })).toHaveText(value) + }) + } + + test('search combines with filters and reset clears both', async ({ page }) => { + await page.goto('/get-started/cookbook') + for (const { name, value } of filters) { + await page.getByRole('button', { name: `${name}: All`, exact: true }).click() + await page.getByRole('menuitemradio', { name: value, exact: true }).click() + } + + const search = page.getByRole('textbox', { name: 'Search examples' }) + await search.fill('workflow') + await expect(page.getByRole('heading', { name: 'Explore 0 examples' })).toBeVisible() + await search.fill('tests') + await expect(page.getByRole('heading', { name: 'Explore 1 examples' })).toBeVisible() + + await page.getByRole('button', { name: 'Reset filters', exact: true }).click() + await expect(search).toHaveValue('') + await expect(page.getByRole('heading', { name: 'Explore 2 examples' })).toBeVisible() + for (const { name } of filters) { + await expect(page.getByRole('button', { name: `${name}: All`, exact: true })).toBeVisible() + } + }) + + for (const key of ['Enter', 'Space']) { + test(`category menu supports ${key} selection and Escape`, async ({ page }) => { + await page.goto('/get-started/cookbook') + const trigger = page.getByRole('button', { name: /^Category:/ }) + await trigger.focus() + await page.keyboard.press(key) + const menu = page.getByRole('menu', { name: 'Category', exact: true }) + await expect(menu.getByRole('menuitemradio', { name: 'All', exact: true })).toBeFocused() + await page.keyboard.press('ArrowDown') + await page.keyboard.press(key) + + await expect(menu).toHaveCount(0) + await expect(trigger).toBeFocused() + await expect(trigger).toContainText('Improve quality and maintainability') + await expect(page.getByRole('heading', { name: 'Explore 1 examples' })).toBeVisible() + await trigger.click() + await page.keyboard.press('Escape') + await expect(menu).toHaveCount(0) + await expect(trigger).toBeFocused() + }) + } + + test('long category options fit within the mobile viewport', async ({ page }) => { + await page.setViewportSize({ width: 320, height: 1000 }) + await page.goto('/get-started/cookbook') + await page.getByRole('button', { name: 'Category: All', exact: true }).click() + const menu = page.getByRole('menu', { name: 'Category', exact: true }) + await expect(menu).toBeVisible() + const bounds = await menu.boundingBox() + expect(bounds).not.toBeNull() + expect(bounds!.x).toBeGreaterThanOrEqual(0) + expect(bounds!.x + bounds!.width).toBeLessThanOrEqual(320) + }) +}) + test.describe('LandingArticleGridWithFilter component', () => { test('category menu selects once via keyboard and preserves URL state', async ({ page }) => { await page.goto('/get-started/article-grid-discovery?articles-filter=Grid&articles-page=2') diff --git a/src/frame/components/lib/action-menu.ts b/src/frame/components/lib/action-menu.ts new file mode 100644 index 000000000000..bb22ca001ba9 --- /dev/null +++ b/src/frame/components/lib/action-menu.ts @@ -0,0 +1,11 @@ +import type { KeyboardEvent } from 'react' + +// Brand's ActionMenu fires onSelect from both the overlay's own Enter listener +// and the focused Item's Enter/Space handler, double-invoking onSelect for one +// keypress. Route both keys through a single click instead. +export function onActionMenuItemKeyDownCapture(event: KeyboardEvent) { + if (event.key !== 'Enter' && event.key !== ' ') return + event.preventDefault() + event.stopPropagation() + event.currentTarget.click() +} diff --git a/src/landings/components/CookBookFilter.module.scss b/src/landings/components/CookBookFilter.module.scss index 7bf7407a36c9..c5d314db8569 100644 --- a/src/landings/components/CookBookFilter.module.scss +++ b/src/landings/components/CookBookFilter.module.scss @@ -1,5 +1,47 @@ -.textInput { - min-width: 100%; +.controls { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.5rem; + padding: 0.25rem; +} + +.search { + flex: 1 1 16rem; + min-width: 0; +} + +.filters { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.5rem; + min-width: 0; + + // Brand adds a wrapper around each trigger and keeps its overlay inline. + > div { + min-width: 0; + max-width: 100%; + } + + [role="menu"] { + width: max-content; + min-width: 0; + max-width: calc(100vw - 2rem); + } + + // Brand's max-content item text otherwise forces long options past the viewport. + :global([class*="ActionMenu__item-text"]) { + width: auto; + } + + // Brand caps the button's label span at 200px and hides overflow, which wraps + // longer labels (like "Category: Author and optimize with Copilot") onto + // multiple lines instead of keeping them on one. + :global([class*="ActionMenu__button"]) > span:first-of-type { + max-width: none; + white-space: nowrap; + } } .categoryLabel { diff --git a/src/landings/components/CookBookFilter.tsx b/src/landings/components/CookBookFilter.tsx index 63a1acc652b0..99add55ce367 100644 --- a/src/landings/components/CookBookFilter.tsx +++ b/src/landings/components/CookBookFilter.tsx @@ -1,8 +1,8 @@ -import { TextInput, ActionMenu, ActionList } from '@primer/react' -import { Button } from '@primer/react-brand' +import { ActionMenu, Button, TextInput } from '@primer/react-brand' import { SearchIcon } from '@primer/octicons-react' import { useRef, useEffect, useState, type ChangeEvent } from 'react' import { ArticleCardItems } from '@/landings/types' +import { onActionMenuItemKeyDownCapture } from '@/frame/components/lib/action-menu' import { useTranslation } from '@/languages/components/useTranslation' import styles from './CookBookFilter.module.scss' @@ -68,15 +68,16 @@ export const CookBookFilter = ({ }, [isSearchOpen]) return ( -
-
+
+
e.preventDefault()}> } placeholder={t('search_articles')} + aria-label={t('search_articles')} ref={inputRef} - autoComplete="false" + autoComplete="off" onChange={(e: ChangeEvent) => { const query = e.target.value || '' onSearch(query) @@ -84,67 +85,79 @@ export const CookBookFilter = ({ />
-
- - +
+ onFilter(categories[Number(value)], 'category', Number(value))} + > + {t('category')}:{' '} {categories[selectedCategory]} - - - {categories.map((category, index) => ( - onFilter(category, 'category', index)} - > - {category} - - ))} - + + {categories.map((category, index) => ( + + {category} + + ))} {showSurface && ( - - + onFilter(surfaces[Number(value)], 'surface', Number(value))} + > + {t('surface')}:{' '} {surfaces[selectedSurface]} - - - {surfaces.map((surface, index) => ( - onFilter(surface, 'surface', index)} - > - {surface} - - ))} - + + {surfaces.map((surface, index) => ( + + {surface} + + ))} )} {showComplexity && ( - - - {t('complexity')}:{' '} + onFilter(complexities[Number(value)], 'complexity', Number(value))} + > + + {t('complexity')}:{' '} {complexities[selectedComplexity]} - - - {complexities.map((complexity, index) => ( - onFilter(complexity, 'complexity', index)} - > - {complexity} - - ))} - + + {complexities.map((complexity, index) => ( + + {complexity} + + ))} )} @@ -152,7 +165,6 @@ export const CookBookFilter = ({