diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/text-counter/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/text-counter/Examples.tsx
index 646d77d0f15..b58a9ab522c 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/text-counter/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/text-counter/Examples.tsx
@@ -26,12 +26,28 @@ export function CountCharactersUp() {
)
}
+export function CountCharactersExceeded() {
+ return (
+
+
+
+ )
+}
+
export function CountCharactersInteractive() {
return (
{() => {
+ const text = 'Count me!'
+ const variant: TextCounterProps['variant'] = 'down'
+ const initialData = {
+ max: 10,
+ variant,
+ text,
+ }
+
const Counter = () => {
- const { data } = Form.useData('text-counter-up', initialData)
+ const { data } = Form.useData('text-counter', initialData)
return (
@@ -59,15 +75,8 @@ export function CountCharactersInteractive() {
)
}
- const variant: TextCounterProps['variant'] = 'down'
- const initialData = {
- max: 10,
- variant,
- text: 'Count me!',
- }
-
return (
-
+
)
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/text-counter/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/text-counter/demos.mdx
index 4a7a14bbad9..28629493d1d 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/text-counter/demos.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/text-counter/demos.mdx
@@ -6,6 +6,10 @@ import * as Examples from './Examples'
## Demos
+### Interactive
+
+
+
### Count characters downwards
@@ -14,6 +18,6 @@ import * as Examples from './Examples'
-### Interactive
+### Show message as exceeded
-
+
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/text-counter/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/text-counter/properties.mdx
index e4a6acc4241..cfacec041b8 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/text-counter/properties.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/text-counter/properties.mdx
@@ -9,5 +9,4 @@ showTabs: true
| `text` | _(required)_ The text to count characters from. |
| `max` | _(required)_ The maximum number of characters allowed. |
| `variant` | _(optional)_ The counting variant. Can be either `up` (counts up from zero) or `down` (counts down from max). Default is `down`. |
-| `bypassAriaLive` | _(optional)_ If true, AriaLive is bypassed. This is useful to signal that a given input field value should not be read out. |
| [Space](/uilib/layout/space/properties) | _(optional)_ spacing properties like `top` or `bottom` are supported. |
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/textarea/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/components/textarea/demos.mdx
index d38fccb2885..2d8d5968aba 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/components/textarea/demos.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/components/textarea/demos.mdx
@@ -43,6 +43,8 @@ import {
### Character counter
+Internally, the [TextCounter](uilib/components/fragments/text-counter/) fragment is used to display the character counter.
+
### With FormStatus failure message
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/textarea/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/components/textarea/properties.mdx
index 2ff00ab72ab..604cee56430 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/components/textarea/properties.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/components/textarea/properties.mdx
@@ -16,7 +16,7 @@ showTabs: true
| `label_sr_only` | _(optional)_ use `true` to make the label only readable by screen readers. |
| `autoresize` | _(optional)_ use `true` to make the Textarea grow and shrink depending on how many lines the user has filled. |
| `autoresize_max_rows` | _(optional)_ set a number to define how many rows the Textarea can auto grow. |
-| `characterCounter` | _(optional)_ use a number to define the displayed max length. You can also use an object defining the [TextCounter](/uilib/fragments/TextCounter/) `variant` or properties. Please avoid using `maxLength` for accessibility reasons. |
+| `characterCounter` | _(optional)_ use a number to define the displayed max length. You can also use an object defining the [TextCounter](uilib/components/fragments/text-counter/) `variant` or properties. Please avoid using `maxLength` for accessibility reasons. |
| `status` | _(optional)_ text with a status message. The style defaults to an error message. You can use `true` to only get the status color, without a message. |
| `status_state` | _(optional)_ defines the state of the status. Currently, there are two statuses `[error, info]`. Defaults to `error`. |
| `status_props` | _(optional)_ use an object to define additional FormStatus properties. |
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/String/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/String/properties.mdx
index c05e25c9ec7..eaf51e73304 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/String/properties.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/String/properties.mdx
@@ -6,28 +6,28 @@ import DataValueReadwriteProperties from '../../data-value-readwrite-properties.
### Component-specific props
-| Property | Type | Description |
-| --------------------------------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `type` | `string` | _(optional)_ Input DOM element type. |
-| `multiline` | `boolean` | _(optional)_ True to be able to write in multiple lines (switching from input-element to textarea-element). |
-| `leftIcon` | `string` | _(optional)_ For icon at the left side of the text input. |
-| `rightIcon` | `string` | _(optional)_ For icon at the right side of the text input. |
-| `inputClassName` | `string` | _(optional)_ Class name set on the <input> DOM element. |
-| `innerRef` | `React.ref` | _(optional)_ by providing a React.ref we can get the internally used input element (DOM). E.g. `innerRef={myRef}` by using `React.createRef()` or `React.useRef()`. |
-| `clear` | `boolean` | _(optional)_ True to have a clickable clear-icon for removing the active value. |
-| `autoresize` | `boolean` | _(optional)_ For `multiline`, set `true` to expand when writing longer texts. |
-| `autoComplete` | `on` or `string` | _(optional)_ For HTML `autocomplete` [attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete). |
-| `capitalize` | `boolean` | _(optional)_ When set to `true`, it will capitalize the first letter of every word, transforming the rest to lowercase. |
-| `trim` | `boolean` | _(optional)_ When `true`, it will trim leading and trailing whitespaces on blur, triggering onChange if the value changes. |
-| `inputMode` | `string` | _(optional)_ Define a [inputmode](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/inputmode). |
-| `autoresizeMaxRows` | `boolean` | _(optional)_ For `multiline`, set how many rows of text can be shown at max. |
-| `characterCounter` | `boolean` or `string` | _(optional)_ For `multiline`, use a number to define the displayed max length. You can also use an object defining the [TextCounter](/uilib/fragments/TextCounter/) `variant` or properties. |
-| `minLength` | `number` | _(optional)_ Validation for minimum length of the text (number of characters). |
-| `maxLength` | `number` | _(optional)_ Validation for maximum length of the text (number of characters). |
-| `pattern` | `string` | _(optional)_ Validation based on regex pattern. |
-| `width` | `string` or `false` | _(optional)_ `false` for no width (use browser default), `small`, `medium` or `large` for predefined standard widths, `stretch` for fill available width. |
-| `help` | `object` | _(optional)_ Provide a help button. Object consisting of `title` and `content`. |
-| [Space](/uilib/layout/space/properties) | Various | _(optional)_ Spacing properties like `top` or `bottom` are supported. |
+| Property | Type | Description |
+| --------------------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `type` | `string` | _(optional)_ Input DOM element type. |
+| `multiline` | `boolean` | _(optional)_ True to be able to write in multiple lines (switching from input-element to textarea-element). |
+| `leftIcon` | `string` | _(optional)_ For icon at the left side of the text input. |
+| `rightIcon` | `string` | _(optional)_ For icon at the right side of the text input. |
+| `inputClassName` | `string` | _(optional)_ Class name set on the <input> DOM element. |
+| `innerRef` | `React.ref` | _(optional)_ by providing a React.ref we can get the internally used input element (DOM). E.g. `innerRef={myRef}` by using `React.createRef()` or `React.useRef()`. |
+| `clear` | `boolean` | _(optional)_ True to have a clickable clear-icon for removing the active value. |
+| `autoresize` | `boolean` | _(optional)_ For `multiline`, set `true` to expand when writing longer texts. |
+| `autoComplete` | `on` or `string` | _(optional)_ For HTML `autocomplete` [attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete). |
+| `capitalize` | `boolean` | _(optional)_ When set to `true`, it will capitalize the first letter of every word, transforming the rest to lowercase. |
+| `trim` | `boolean` | _(optional)_ When `true`, it will trim leading and trailing whitespaces on blur, triggering onChange if the value changes. |
+| `inputMode` | `string` | _(optional)_ Define a [inputmode](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/inputmode). |
+| `autoresizeMaxRows` | `boolean` | _(optional)_ For `multiline`, set how many rows of text can be shown at max. |
+| `characterCounter` | `boolean` or `string` | _(optional)_ For `multiline`, use a number to define the displayed max length. You can also use an object defining the [TextCounter](uilib/components/fragments/text-counter/) `variant` or properties. |
+| `minLength` | `number` | _(optional)_ Validation for minimum length of the text (number of characters). |
+| `maxLength` | `number` | _(optional)_ Validation for maximum length of the text (number of characters). |
+| `pattern` | `string` | _(optional)_ Validation based on regex pattern. |
+| `width` | `string` or `false` | _(optional)_ `false` for no width (use browser default), `small`, `medium` or `large` for predefined standard widths, `stretch` for fill available width. |
+| `help` | `object` | _(optional)_ Provide a help button. Object consisting of `title` and `content`. |
+| [Space](/uilib/layout/space/properties) | Various | _(optional)_ Spacing properties like `top` or `bottom` are supported. |
## Properties
diff --git a/packages/dnb-eufemia/src/components/aria-live/AriaLive.tsx b/packages/dnb-eufemia/src/components/aria-live/AriaLive.tsx
index d966dd69bc5..4d6d208fc4c 100644
--- a/packages/dnb-eufemia/src/components/aria-live/AriaLive.tsx
+++ b/packages/dnb-eufemia/src/components/aria-live/AriaLive.tsx
@@ -2,10 +2,11 @@ import React from 'react'
import { AriaLiveAllProps } from './types'
import useAriaLive from './useAriaLive'
-export default function AriaLive(props: AriaLiveAllProps) {
+export default function AriaLive({ element, ...props }: AriaLiveAllProps) {
const ariaAttributes = useAriaLive(props)
+ const Element = element || 'section'
- return
+ return
}
AriaLive._supportsSpacingProps = 'children'
diff --git a/packages/dnb-eufemia/src/components/aria-live/__tests__/AriaLive.test.tsx b/packages/dnb-eufemia/src/components/aria-live/__tests__/AriaLive.test.tsx
index 4d117084276..40225bba922 100644
--- a/packages/dnb-eufemia/src/components/aria-live/__tests__/AriaLive.test.tsx
+++ b/packages/dnb-eufemia/src/components/aria-live/__tests__/AriaLive.test.tsx
@@ -110,6 +110,24 @@ describe('AriaLive', () => {
expect(element).toHaveAttribute('data-test', 'test-data')
})
+ it('should reset (remove) given message after a while', async () => {
+ render(
+
+ Announcement
+
+ )
+
+ const element = document.querySelector('.dnb-aria-live')
+
+ await waitFor(() => {
+ expect(element).toHaveTextContent('Announcement')
+ })
+
+ await waitFor(() => {
+ expect(element).toHaveTextContent('')
+ })
+ })
+
it('should have dnb-sr-only class', () => {
render(
diff --git a/packages/dnb-eufemia/src/components/aria-live/types.ts b/packages/dnb-eufemia/src/components/aria-live/types.ts
index e4631b28e40..87b17ad660f 100644
--- a/packages/dnb-eufemia/src/components/aria-live/types.ts
+++ b/packages/dnb-eufemia/src/components/aria-live/types.ts
@@ -1,4 +1,6 @@
export type AriaLiveProps = {
+ element?: React.ElementType
+
/**
* The variant of the announcement. Can be 'text' or 'content'.
*/
diff --git a/packages/dnb-eufemia/src/components/aria-live/useAriaLive.tsx b/packages/dnb-eufemia/src/components/aria-live/useAriaLive.tsx
index 3618d64b2ab..209cd8e38eb 100644
--- a/packages/dnb-eufemia/src/components/aria-live/useAriaLive.tsx
+++ b/packages/dnb-eufemia/src/components/aria-live/useAriaLive.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useState } from 'react'
+import React, { useEffect, useRef, useState } from 'react'
import classnames from 'classnames'
import { AriaLiveAllProps } from './types'
import { extendPropsWithContext } from '../../shared/component-helper'
@@ -37,6 +37,7 @@ const priorityConfig: {
export default function useAriaLive(props: AriaLiveAllProps) {
const [announcement, setAnnouncement] = useState('')
+ const timeoutRef = useRef(null)
const {
disabled = false,
@@ -56,21 +57,37 @@ export default function useAriaLive(props: AriaLiveAllProps) {
priorityConfig[props.priority || 'low']
)
+ const showTextAnnouncement = delay > -1
+
useEffect(() => {
- if (delay > -1) {
+ if (showTextAnnouncement) {
setAnnouncement('')
+ const isTest = process.env.NODE_ENV === 'test'
const timer = setTimeout(
- () => setAnnouncement(disabled ? '' : children),
- (process.env.NODE_ENV === 'test' ? 0 : delay) ?? 1000
+ () => {
+ if (!disabled) {
+ setAnnouncement(children)
+ }
+
+ clearTimeout(timeoutRef.current)
+ timeoutRef.current = setTimeout(
+ () => setAnnouncement(''),
+ isTest ? 100 : delay + 1000
+ )
+ },
+ (isTest ? 0 : delay) ?? 1000
)
- return () => clearTimeout(timer)
+ return () => {
+ clearTimeout(timer)
+ clearTimeout(timeoutRef.current)
+ }
}
- }, [disabled, delay, children])
+ }, [delay, children, disabled, showTextAnnouncement])
return {
- 'aria-live': !disabled ? politeness : 'off',
+ 'aria-live': disabled && !showTextAnnouncement ? 'off' : politeness,
'aria-atomic': atomic,
'aria-relevant': relevant,
className: classnames(
@@ -78,7 +95,7 @@ export default function useAriaLive(props: AriaLiveAllProps) {
!showAnnouncement && 'dnb-sr-only',
className
),
- children: delay > -1 ? announcement : children,
+ children: showTextAnnouncement ? announcement : children,
...rest,
}
}
diff --git a/packages/dnb-eufemia/src/components/textarea/Textarea.js b/packages/dnb-eufemia/src/components/textarea/Textarea.js
index 01790ec997e..0349b13b1bf 100644
--- a/packages/dnb-eufemia/src/components/textarea/Textarea.js
+++ b/packages/dnb-eufemia/src/components/textarea/Textarea.js
@@ -550,9 +550,9 @@ export default class Textarea extends React.PureComponent {
{characterCounter && (
{
)
- const counter = document.querySelector('.dnb-text-counter__message')
+ const counter = document.querySelector('.dnb-text-counter')
const textarea = document.querySelector('textarea')
const ariaLive = document.querySelector('.dnb-aria-live')
- expect(counter).toHaveTextContent('3 av 8 tegn gjenstår')
+ expect(counter).toHaveTextContent('5 av 8 tegn gjenstår')
expect(ariaLive).toHaveTextContent('')
await userEvent.type(textarea, 'bar')
- expect(counter).toHaveTextContent('6 av 8 tegn gjenstår')
- expect(ariaLive).toHaveTextContent('6 av 8 tegn gjenstår')
+ expect(counter).toHaveTextContent('2 av 8 tegn gjenstår')
+ expect(ariaLive).toHaveTextContent('2 av 8 tegn gjenstår')
rerender(
)
- expect(counter).toHaveTextContent('6 of 8 characters remaining')
+ expect(counter).toHaveTextContent('2 of 8 characters remaining')
await userEvent.type(textarea, 'baz')
- expect(ariaLive).toHaveTextContent('8 of 8 characters remaining')
+ expect(ariaLive).toHaveTextContent(
+ 'You have exceeded the limit by 1 on 8 characters'
+ )
+
+ rerender(
+
+ )
+
+ expect(counter).toHaveTextContent(
+ 'You have exceeded the limit by 1 on 8 characters'
+ )
rerender(
)
}
diff --git a/packages/dnb-eufemia/src/fragments/text-counter/__tests__/TextCounter.screenshot.test.ts b/packages/dnb-eufemia/src/fragments/text-counter/__tests__/TextCounter.screenshot.test.ts
index 3d43ec727cc..7e0adc4dee5 100644
--- a/packages/dnb-eufemia/src/fragments/text-counter/__tests__/TextCounter.screenshot.test.ts
+++ b/packages/dnb-eufemia/src/fragments/text-counter/__tests__/TextCounter.screenshot.test.ts
@@ -27,4 +27,11 @@ describe.each(['ui', 'sbanken'])('TextCounter for %s', (themeName) => {
})
expect(screenshot).toMatchImageSnapshot()
})
+
+ it('have to character counter exceeded', async () => {
+ const screenshot = await makeScreenshot({
+ selector: '[data-visual-test="text-counter-exceeded"]',
+ })
+ expect(screenshot).toMatchImageSnapshot()
+ })
})
diff --git a/packages/dnb-eufemia/src/fragments/text-counter/__tests__/TextCounter.test.tsx b/packages/dnb-eufemia/src/fragments/text-counter/__tests__/TextCounter.test.tsx
index eb223a59a9f..f18045861ce 100644
--- a/packages/dnb-eufemia/src/fragments/text-counter/__tests__/TextCounter.test.tsx
+++ b/packages/dnb-eufemia/src/fragments/text-counter/__tests__/TextCounter.test.tsx
@@ -14,65 +14,56 @@ describe('TextCounter', () => {
it('displays the correct count when variant is not specified', () => {
render()
- const element = document.querySelector('.dnb-text-counter__message')
- expect(element).toHaveTextContent('4 av 10 tegn gjenstår')
+ const element = document.querySelector('.dnb-text-counter')
+ expect(element).toHaveTextContent('6 av 10 tegn gjenstår')
})
it('displays the correct count for variant "down"', () => {
render()
- const element = document.querySelector('.dnb-text-counter__message')
+ const element = document.querySelector('.dnb-text-counter')
expect(element).toHaveTextContent('6 av 10 tegn gjenstår')
})
it('displays the correct count for variant "up"', () => {
render()
- const element = document.querySelector('.dnb-text-counter__message')
+ const element = document.querySelector('.dnb-text-counter')
expect(element).toHaveTextContent('Du har brukt 4 av 10 tegn')
})
- it('does disable aria-live when bypassAriaLive', () => {
- const { rerender } = render()
-
- const aria = document.querySelector('.dnb-aria-live')
- expect(aria).toHaveAttribute('aria-live', 'polite')
-
- rerender()
-
- expect(aria).toHaveAttribute('aria-live', 'off')
- })
-
it('handles empty text correctly', () => {
render()
- const element = document.querySelector('.dnb-text-counter__message')
+ const element = document.querySelector('.dnb-text-counter')
expect(element).toHaveTextContent('0 av 10 tegn gjenstår')
})
it('handles text length exceeding max correctly', () => {
render()
- const element = document.querySelector('.dnb-text-counter__message')
- expect(element).toHaveTextContent('10 av 10 tegn gjenstår')
+ const element = document.querySelector('.dnb-text-counter')
+ expect(element).toHaveTextContent(
+ 'Du har brukt 9 tegn mer enn maksgrensen på 10'
+ )
})
it('handles negative max correctly', () => {
render()
- const element = document.querySelector('.dnb-text-counter__message')
+ const element = document.querySelector('.dnb-text-counter')
expect(element).toHaveTextContent('')
})
it('updates correctly when props change', () => {
const { rerender } = render()
- const element = document.querySelector('.dnb-text-counter__message')
- expect(element).toHaveTextContent('4 av 10 tegn gjenstår')
+ const element = document.querySelector('.dnb-text-counter')
+ expect(element).toHaveTextContent('6 av 10 tegn gjenstår')
rerender()
- expect(element).toHaveTextContent('7 av 10 tegn gjenstår')
+ expect(element).toHaveTextContent('3 av 10 tegn gjenstår')
})
it('supports lang and locale props', () => {
@@ -82,9 +73,9 @@ describe('TextCounter', () => {
)
- const element = document.querySelector('.dnb-text-counter__message')
+ const element = document.querySelector('.dnb-text-counter')
- expect(element).toHaveTextContent('4 of 10 characters remaining')
+ expect(element).toHaveTextContent('6 of 10 characters remaining')
rerender(
@@ -92,7 +83,7 @@ describe('TextCounter', () => {
)
- expect(element).toHaveTextContent('4 of 10 characters remaining')
+ expect(element).toHaveTextContent('6 of 10 characters remaining')
rerender(
@@ -100,7 +91,7 @@ describe('TextCounter', () => {
)
- expect(element).toHaveTextContent('4 of 10 characters remaining')
+ expect(element).toHaveTextContent('6 of 10 characters remaining')
rerender(
@@ -108,38 +99,65 @@ describe('TextCounter', () => {
)
- expect(element).toHaveTextContent('4 av 10 tegn gjenstår')
+ expect(element).toHaveTextContent('6 av 10 tegn gjenstår')
})
- it('should render AriaLive message', async () => {
- const { rerender } = render(
-
+ it('should have exceeded class when text lengths exceeds the max', () => {
+ const { rerender } = render()
+
+ const element = document.querySelector('.dnb-text-counter')
+ expect(element).not.toHaveClass('dnb-text-counter--exceeded')
+ expect(element).toHaveTextContent('0 av 4 tegn gjenstår')
+
+ rerender()
+
+ expect(element).toHaveClass('dnb-text-counter--exceeded')
+ expect(element).toHaveTextContent(
+ 'Du har brukt 3 tegn mer enn maksgrensen på 4'
)
- const counter = document.querySelector('.dnb-text-counter__message')
+ rerender()
+
+ expect(element).toHaveClass('dnb-text-counter--exceeded')
+ expect(element).toHaveTextContent(
+ 'Du har brukt 7 tegn mer enn maksgrensen på 4'
+ )
+ })
+
+ it('should have empty AriaLive message before a text change is made', () => {
+ const { rerender } = render()
+
+ const aria = document.querySelector('.dnb-aria-live')
+ expect(aria).toHaveAttribute('aria-live', 'polite')
+
+ rerender()
+
+ expect(aria).toHaveAttribute('aria-live', 'polite')
+ expect(aria).toHaveTextContent('')
+ })
+
+ it('should render AriaLive message', async () => {
+ const { rerender } = render()
+
+ const counter = document.querySelector('.dnb-text-counter')
const ariaLive = document.querySelector('.dnb-aria-live')
- expect(counter).toHaveTextContent('0 av 8 tegn gjenstår')
+ expect(counter).toHaveTextContent('8 av 8 tegn gjenstår')
expect(ariaLive).toHaveTextContent('')
- rerender()
+ rerender()
- expect(counter).toHaveTextContent('3 av 8 tegn gjenstår')
+ expect(counter).toHaveTextContent('5 av 8 tegn gjenstår')
await waitFor(() => {
- expect(ariaLive).toHaveTextContent('3 av 8 tegn gjenstår')
+ expect(ariaLive).toHaveTextContent('5 av 8 tegn gjenstår')
})
- rerender(
-
- )
+ rerender()
- expect(counter).toHaveTextContent('8 of 8 characters remaining')
+ expect(counter).toHaveTextContent(
+ 'You have exceeded the limit by 3 on 8 characters'
+ )
await waitFor(() => {
expect(ariaLive).toHaveTextContent('')
@@ -152,4 +170,18 @@ describe('TextCounter', () => {
const element = document.querySelector('.dnb-text-counter')
expect(element).toHaveClass('dnb-space__top--large')
})
+
+ it('supports accept custom class', () => {
+ render()
+
+ const element = document.querySelector('.dnb-text-counter')
+ expect(element).toHaveClass('custom-class')
+ })
+
+ it('should have paragraph element', () => {
+ render()
+
+ const element = document.querySelector('.dnb-text-counter')
+ expect(element.tagName).toBe('P')
+ })
})
diff --git a/packages/dnb-eufemia/src/fragments/text-counter/__tests__/__image_snapshots__/textcounter-for-sbanken-have-to-character-counter-downwards.snap.png b/packages/dnb-eufemia/src/fragments/text-counter/__tests__/__image_snapshots__/textcounter-for-sbanken-have-to-character-counter-downwards.snap.png
index bd80baf94ea..667e2a00bfd 100644
Binary files a/packages/dnb-eufemia/src/fragments/text-counter/__tests__/__image_snapshots__/textcounter-for-sbanken-have-to-character-counter-downwards.snap.png and b/packages/dnb-eufemia/src/fragments/text-counter/__tests__/__image_snapshots__/textcounter-for-sbanken-have-to-character-counter-downwards.snap.png differ
diff --git a/packages/dnb-eufemia/src/fragments/text-counter/__tests__/__image_snapshots__/textcounter-for-sbanken-have-to-character-counter-exceeded.snap.png b/packages/dnb-eufemia/src/fragments/text-counter/__tests__/__image_snapshots__/textcounter-for-sbanken-have-to-character-counter-exceeded.snap.png
new file mode 100644
index 00000000000..89093ee3805
Binary files /dev/null and b/packages/dnb-eufemia/src/fragments/text-counter/__tests__/__image_snapshots__/textcounter-for-sbanken-have-to-character-counter-exceeded.snap.png differ
diff --git a/packages/dnb-eufemia/src/fragments/text-counter/__tests__/__image_snapshots__/textcounter-for-sbanken-have-to-character-counter-upwards.snap.png b/packages/dnb-eufemia/src/fragments/text-counter/__tests__/__image_snapshots__/textcounter-for-sbanken-have-to-character-counter-upwards.snap.png
index 19e3b378f43..7897c43c142 100644
Binary files a/packages/dnb-eufemia/src/fragments/text-counter/__tests__/__image_snapshots__/textcounter-for-sbanken-have-to-character-counter-upwards.snap.png and b/packages/dnb-eufemia/src/fragments/text-counter/__tests__/__image_snapshots__/textcounter-for-sbanken-have-to-character-counter-upwards.snap.png differ
diff --git a/packages/dnb-eufemia/src/fragments/text-counter/__tests__/__image_snapshots__/textcounter-for-ui-have-to-character-counter-downwards.snap.png b/packages/dnb-eufemia/src/fragments/text-counter/__tests__/__image_snapshots__/textcounter-for-ui-have-to-character-counter-downwards.snap.png
index 67a454879e1..793e70294aa 100644
Binary files a/packages/dnb-eufemia/src/fragments/text-counter/__tests__/__image_snapshots__/textcounter-for-ui-have-to-character-counter-downwards.snap.png and b/packages/dnb-eufemia/src/fragments/text-counter/__tests__/__image_snapshots__/textcounter-for-ui-have-to-character-counter-downwards.snap.png differ
diff --git a/packages/dnb-eufemia/src/fragments/text-counter/__tests__/__image_snapshots__/textcounter-for-ui-have-to-character-counter-exceeded.snap.png b/packages/dnb-eufemia/src/fragments/text-counter/__tests__/__image_snapshots__/textcounter-for-ui-have-to-character-counter-exceeded.snap.png
new file mode 100644
index 00000000000..163efd6ab60
Binary files /dev/null and b/packages/dnb-eufemia/src/fragments/text-counter/__tests__/__image_snapshots__/textcounter-for-ui-have-to-character-counter-exceeded.snap.png differ
diff --git a/packages/dnb-eufemia/src/fragments/text-counter/__tests__/__image_snapshots__/textcounter-for-ui-have-to-character-counter-upwards.snap.png b/packages/dnb-eufemia/src/fragments/text-counter/__tests__/__image_snapshots__/textcounter-for-ui-have-to-character-counter-upwards.snap.png
index efdf7d2a7d1..7f29e1d7421 100644
Binary files a/packages/dnb-eufemia/src/fragments/text-counter/__tests__/__image_snapshots__/textcounter-for-ui-have-to-character-counter-upwards.snap.png and b/packages/dnb-eufemia/src/fragments/text-counter/__tests__/__image_snapshots__/textcounter-for-ui-have-to-character-counter-upwards.snap.png differ
diff --git a/packages/dnb-eufemia/src/fragments/text-counter/style/dnb-text-counter.scss b/packages/dnb-eufemia/src/fragments/text-counter/style/dnb-text-counter.scss
index 02da6ddd3d9..34d1f28e7cb 100644
--- a/packages/dnb-eufemia/src/fragments/text-counter/style/dnb-text-counter.scss
+++ b/packages/dnb-eufemia/src/fragments/text-counter/style/dnb-text-counter.scss
@@ -1,3 +1,15 @@
.dnb-text-counter {
- word-break: break-word;
+ .dnb-icon {
+ margin-right: 0;
+
+ font-size: 0;
+ transition:
+ margin 200ms var(--easing-default),
+ font-size 400ms var(--easing-default);
+ }
+ &--exceeded .dnb-icon {
+ margin-right: 0.5rem;
+
+ font-size: var(--font-size-basis--em);
+ }
}
diff --git a/packages/dnb-eufemia/src/fragments/text-counter/style/themes/dnb-text-counter-theme-sbanken.scss b/packages/dnb-eufemia/src/fragments/text-counter/style/themes/dnb-text-counter-theme-sbanken.scss
index 629d74edf9f..f203a00ff01 100644
--- a/packages/dnb-eufemia/src/fragments/text-counter/style/themes/dnb-text-counter-theme-sbanken.scss
+++ b/packages/dnb-eufemia/src/fragments/text-counter/style/themes/dnb-text-counter-theme-sbanken.scss
@@ -1,6 +1,8 @@
.dnb-text-counter {
- &__message {
- font-size: var(--font-size-small);
+ &.dnb-p {
color: var(--sb-color-gray-dark);
}
+ &--exceeded.dnb-p {
+ color: var(--sb-color-red);
+ }
}
diff --git a/packages/dnb-eufemia/src/fragments/text-counter/style/themes/dnb-text-counter-theme-ui.scss b/packages/dnb-eufemia/src/fragments/text-counter/style/themes/dnb-text-counter-theme-ui.scss
index 1450971b552..b946ad0f21d 100644
--- a/packages/dnb-eufemia/src/fragments/text-counter/style/themes/dnb-text-counter-theme-ui.scss
+++ b/packages/dnb-eufemia/src/fragments/text-counter/style/themes/dnb-text-counter-theme-ui.scss
@@ -1,6 +1,8 @@
.dnb-text-counter {
- &__message {
- font-size: var(--font-size-small);
+ &.dnb-p {
color: var(--color-black-55);
}
+ &--exceeded.dnb-p {
+ color: var(--color-fire-red);
+ }
}
diff --git a/packages/dnb-eufemia/src/shared/locales/en-GB.js b/packages/dnb-eufemia/src/shared/locales/en-GB.js
index 2f2dd8998fb..5215dafff4d 100644
--- a/packages/dnb-eufemia/src/shared/locales/en-GB.js
+++ b/packages/dnb-eufemia/src/shared/locales/en-GB.js
@@ -3,6 +3,8 @@ export default {
TextCounter: {
characterDown: '%count of %max characters remaining',
characterUp: 'You have used %count of %max characters',
+ characterExceeded:
+ 'You have exceeded the limit by %count on %max characters',
},
TimelineItem: {
alt_label_completed: 'Complete',
diff --git a/packages/dnb-eufemia/src/shared/locales/nb-NO.js b/packages/dnb-eufemia/src/shared/locales/nb-NO.js
index d03ccc62a80..530132c9784 100644
--- a/packages/dnb-eufemia/src/shared/locales/nb-NO.js
+++ b/packages/dnb-eufemia/src/shared/locales/nb-NO.js
@@ -3,6 +3,8 @@ export default {
TextCounter: {
characterDown: '%count av %max tegn gjenstår',
characterUp: 'Du har brukt %count av %max tegn',
+ characterExceeded:
+ 'Du har brukt %count tegn mer enn maksgrensen på %max',
},
TimelineItem: {
alt_label_completed: 'Utført',