Skip to content

feat(Search): use ds-suggestion under the hood to support clear button in pure html - #5126

Open
unekinn wants to merge 16 commits into
mainfrom
feat/web-search-clear
Open

feat(Search): use ds-suggestion under the hood to support clear button in pure html#5126
unekinn wants to merge 16 commits into
mainfrom
feat/web-search-clear

Conversation

@unekinn

@unekinn unekinn commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a @digdir/designsystemet-web/search module (also imported at @digdir/designsystemet-web) which clears and focuses the sibling <input> when a .ds-search button[type="reset"] is clicked. Bails out when the click was already handled (e.g. by React's Search.Clear).

Checks

Clears and focuses the sibling `<input>` when a `.ds-search button[type="reset"]` is clicked.
Bails out when the click was already handled (e.g. by React's `Search.Clear`).
@changeset-bot

changeset-bot Bot commented Jul 31, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 1c57702

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 5 packages
Name Type
@digdir/designsystemet-web Minor
@digdir/designsystemet-react Minor
@digdir/designsystemet-css Minor
@digdir/designsystemet Minor
@digdir/designsystemet-types Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Preview deployments for this pull request:

storybook - 5. Aug 2026 - 19:06

www - 5. Aug 2026 - 19:09

@eirikbacker
eirikbacker marked this pull request as draft July 31, 2026 08:09
@eirikbacker
eirikbacker marked this pull request as ready for review August 4, 2026 07:50
@eirikbacker

eirikbacker commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Note, this solution now looks for a button with type reset and with no content, placed directly after a input with type search. It bails out if the click event is already prevented (i.e. like Suggestion.Clear does).

This makes the functionality independent of CSS (making JSDOM tests happy), but it will also potentially catch non-Designsystemet components, if all conditions are met, which is problematic.

In my opinion, I think we should consider removing the Search component entirely, and rather add a magnifying icon on the input if type is set to search in the Suggestion component.

Maybe we should also as well as supporting a "Søk" button, but I must also admit I personally do not see why the search button is connected to the input. This could be a wrapper component (for example "Toolbar") instead, being able to "connect" buttons and/or inputs placed next to each other if we'd like that. This would not make a "special case" button styling for a single component, but enable our consumers to mix and match as needed – IF we feel like we should ship a connected input-button at all that is..
Is the connected input+button is more of an opinionated visual preference, than a component/functionality everyone probably needs? In Mattilsynet, we intentionally do not connect any fields, but rather keep a small distance, simplifying our code and visual appearance: https://www.mattilsynet.no/ (click "Søk" in the menu to see an example) ☺️

@eirikbacker

Copy link
Copy Markdown
Contributor

Updated the code to use data-search="clear" instead of type="reset" to be more specific in what we target ☺️

@Barsnes

Barsnes commented Aug 4, 2026

Copy link
Copy Markdown
Member
image https://www-pr-5126.victoriousglacier-eb9399b9.norwayeast.azurecontainerapps.io/no/components/docs/search/overview image

@Barsnes Barsnes left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Add a changeset about the internals of react search.clear changing and we are good 🔥

Comment thread packages/css/src/search.css Outdated
@Barsnes

Barsnes commented Aug 4, 2026

Copy link
Copy Markdown
Member

Updated the code to use data-search="clear" instead of type="reset" to be more specific in what we target ☺️

Right, but this would break people who have implemented this themselves, since it would not target their reset button anymore

@eirikbacker
eirikbacker requested review from Barsnes and a lite review from Copilot August 4, 2026 12:32

This comment was marked as outdated.

@eirikbacker
eirikbacker marked this pull request as draft August 5, 2026 08:16
@eirikbacker
eirikbacker requested a lite review from Copilot August 5, 2026 16:29
@eirikbacker eirikbacker changed the title feat(web): add a search module so Search.Clear works without React feat(Search): use ds-suggestion under the hood to support clear button in pure html Aug 5, 2026
@eirikbacker

Copy link
Copy Markdown
Contributor
  • Updated Search to use <ds-suggestion> under the hood
  • Extended suggestion.css to include magnifying glass, and thus removing the need for search.css
  • Updated Search so magnifying glass is not bound to "Søk"-button, but instead type of input, giving more control to the end user
  • Updated suggestion.ts to no longer set a empty placeholder as u-combobox now makes this redundant
  • Simplified the suggestion.css to reduce :has() selectors and thus getting better performance
  • Simplified the places [lang] selectors was used instead og :lang()
  • Question: Should we really provide an ugly linear-gradient based chevron, when button[aria-expanded] does not exist? The browser does not.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 19 out of 20 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file
Suppressed comments (5)

packages/react/src/components/search/search-clear.tsx:31

  • SearchClear currently renders a plain type="reset" button without any behavior. Outside of a form, reset does nothing, so existing expectations (and the component docs) that the clear button clears and focuses the input will break. Consider reinstating the click handler (and bailing out if a consumer already handled the click via preventDefault), so React usage keeps working even when the web-side behavior isn’t present.
export const SearchClear = forwardRef<HTMLButtonElement, SearchClearProps>(
  function SearchClear({ 'aria-label': label = 'Tøm', ...rest }, ref) {
    return (
      <Button
        aria-label={label}
        ref={ref}
        type='reset'
        variant='tertiary'
        {...rest}
      />

packages/css/src/suggestion.css:345

  • Typo in comment: "exsist" should be "exist".
  /* Draw chevron even when input is empty and button[aria-expanded] does not exsist */

apps/www/app/content/components/search/no/code.mdx:7

  • The docs say any wrapper with class "ds-search" will get an automatically shown/hidden clear button based on input value. In this PR, the search behavior is tied to / web JS; there is no CSS-only rule left that can toggle visibility based on value. This guidance is likely misleading for plain HTML wrappers.
Bruk klassenavnet `ds-search` på et wrapper-element rundt `<input>`.
Vil du ha en tøm-knapp, legger du til en `<button type="reset">` etter `<input>`. Denne vises og skjules automatisk basert på verdien i input.
Vil du ha en ekspander-knapp, legger du til en `<button aria-expanded="false">` etter `<input>`.

apps/www/app/content/components/search/en/code.mdx:7

  • These docs describe a CSS-only pattern (any wrapper with class "ds-search") but also claim the clear button is automatically shown/hidden based on the input value. With search.css removed and no remaining CSS selector for value-dependent toggling, this appears to require / web JS instead, so the current wording is misleading.
Use the class name `ds-search` on a wrapper element around `<input>`.
If you want a clear button, add a `<button type="reset">` after the `<input>`. It is automatically shown and hidden based on the input's value.
If you want an expand-button, add a `<button aria-expanded="false">` after the `<input>`.

.changeset/slick-rockets-build.md:7

  • The changeset claims the clear button works “also without React”, but this PR currently doesn’t include/ship the promised web search module (and the new web test has no implementation to exercise). Either the missing web module needs to be added/wired up, or this note should be softened to avoid overstating the release behavior.
**Search:** `Search` is now built on top of `<ds-suggestion>` supporting clear button also without React

Comment thread packages/web/src/search/search.test.ts Outdated
Comment thread packages/react/src/components/search/search-clear.tsx
Comment thread packages/react/src/components/search/search.tsx

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 19 out of 20 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file
Suppressed comments (2)

packages/web/src/search/search.test.ts:5

  • The PR description says it adds a @digdir/designsystemet-web/search module that handles .ds-search button[type="reset"] clicks, but the only file under packages/web/src/search in this PR is this test, and packages/web/src/index.ts doesn’t import/export any search module. If consumers are expected to import @digdir/designsystemet-web/search, the runtime module/export is missing; otherwise the PR description (and any intended public API) should be updated to reflect that the behavior comes from <ds-suggestion>/@u-elements/u-combobox instead of a new designsystemet-web module.
/// <reference types="@testing-library/jest-dom" />

import { describe, expect, it } from 'vitest';

describe('search behavior', () => {

packages/react/src/components/search/search.tsx:32

  • <ds-suggestion> is a custom element, but this component uses className. In this codebase other wrappers use class={...} because React does not reliably translate className for custom elements (see suggestion.tsx). This can prevent the ds-search class from being applied, breaking styling/behavior that depends on it. Also add suppressHydrationWarning to match other custom-element wrappers.
      <ds-suggestion
        ref={ref}
        className={cl('ds-search', className)}
        {...rest}
      />

@eirikbacker
eirikbacker marked this pull request as ready for review August 5, 2026 17:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants