Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Hint as component and Label and FieldSet props #1499

Merged
merged 54 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
32033bb
WIP: field sets can be marked as not required
RubenSibon Jul 12, 2024
db41cd4
Merge branch 'develop' of github.com:Amsterdam/design-system into fea…
RubenSibon Jul 24, 2024
21b8b37
Merge branch 'main' of github.com:Amsterdam/design-system into feat/D…
RubenSibon Aug 7, 2024
face4f9
Merge branch 'develop' of github.com:Amsterdam/design-system into fea…
RubenSibon Aug 7, 2024
d776a24
feat(label): 💄 apply not-required label design
RubenSibon Aug 7, 2024
20f4a14
refactor(scss):♻️ declarations before nestings
RubenSibon Aug 7, 2024
955bc08
docs(stories): 📖 label and field w/ req props
RubenSibon Aug 7, 2024
f2bd856
docs(label): 🏷️ how to show field is optional
RubenSibon Aug 7, 2024
8a0eaf1
fix(label): explicit required prop
RubenSibon Aug 7, 2024
5151b5d
Merge branch 'develop' into feat/DES-844-form-field-not-required
VincentSmedinga Aug 30, 2024
51497f2
Merge branch 'main' of github.com:Amsterdam/design-system into feat/D…
RubenSibon Sep 4, 2024
22c021f
Merge branch 'develop' of github.com:Amsterdam/design-system into fea…
RubenSibon Sep 4, 2024
49fd367
Merge branch 'feat/DES-844-form-field-not-required' of github.com:Ams…
RubenSibon Sep 4, 2024
efaac88
docs(form): prop comments
RubenSibon Sep 4, 2024
ca9d11e
refactor(label): 🏷️ optionality and hint text
RubenSibon Sep 4, 2024
3637f7e
refactor(field-set): ⏹️ optionality and hint text
RubenSibon Sep 5, 2024
b66c296
Merge branch 'develop' of github.com:Amsterdam/design-system into fea…
RubenSibon Sep 5, 2024
a78e521
docs(field-set): heading level
RubenSibon Sep 5, 2024
4bdbef5
refactor(css): override block at bottom
RubenSibon Sep 6, 2024
062cff4
feat(hint): 🌙 non-breaking space
RubenSibon Sep 6, 2024
3e7ba28
feat(hint): ℹ️ only wrap text in the spans
RubenSibon Sep 6, 2024
ef13f6e
test(field-set): update texts
RubenSibon Sep 6, 2024
eef4c08
docs(hint): ℹ️ required and optional fields
RubenSibon Sep 6, 2024
e885ca6
feat(hint): 🧠 add hint component
RubenSibon Sep 6, 2024
e6f6508
docs(hint): 📄 stories and docs
RubenSibon Sep 6, 2024
773f5cb
docs(field-set): 📄 address issues
RubenSibon Sep 6, 2024
fad03ea
docs(hint): 📖 make story clearer
RubenSibon Sep 6, 2024
5e87e00
fix(test): missed rename after copying
RubenSibon Sep 6, 2024
8900baf
docs(hint): simplify docs
RubenSibon Sep 6, 2024
a03275f
docs(hint): shorter description
RubenSibon Sep 6, 2024
fbcca7f
docs(label): simplify docs
RubenSibon Sep 6, 2024
767b919
test(field-set): shorter test description
RubenSibon Sep 6, 2024
606817b
test(field-set): test for presence
RubenSibon Sep 6, 2024
15bb4d7
docs(field-set): 📄 fix and address issues
RubenSibon Sep 6, 2024
2d50c24
Merge branch 'feat/DES-844-form-field-not-required' of github.com:Ams…
RubenSibon Sep 6, 2024
eb0f64c
docs(field-set): simplify comment
RubenSibon Sep 6, 2024
0ab3593
test(field-set): descriptions and light mods
RubenSibon Sep 6, 2024
9dfd2af
refactor(hint): shared hint fn
RubenSibon Sep 6, 2024
6ece317
Merge branch 'develop' of github.com:Amsterdam/design-system into fea…
RubenSibon Sep 6, 2024
e3ba6f0
fix(hint): ♾️ recursive fn and tests
RubenSibon Sep 10, 2024
e4410a4
Merge branch 'develop' of github.com:Amsterdam/design-system into fea…
RubenSibon Sep 18, 2024
8d2701f
refactor(hint): use inline-block for text breakingn
RubenSibon Sep 18, 2024
e278ba6
feat(hint): 🗑️ remove hint story & docs
RubenSibon Sep 18, 2024
74a3def
refactor(test): remove explicity true
RubenSibon Sep 18, 2024
7d11d11
fix(field-set): field-set req if all fields req
RubenSibon Sep 18, 2024
2c97260
refactor(test): consistent queries and naming
RubenSibon Sep 18, 2024
941312f
refactor(field-set): sort props
RubenSibon Sep 18, 2024
511a613
refactor(ts): extract props from element props
RubenSibon Sep 18, 2024
e67a7a4
fix(field-set): only req on field sets with role
RubenSibon Sep 18, 2024
f894b29
Merge branch 'develop' of github.com:Amsterdam/design-system into fea…
RubenSibon Sep 18, 2024
f88e5b4
refactor: explicit aria-required="true"
RubenSibon Sep 20, 2024
ed9708d
test(field-set): use getByRole
RubenSibon Sep 20, 2024
49725eb
test(field-set): use getByRole
RubenSibon Sep 20, 2024
6841dea
Merge branch 'feat/DES-844-form-field-not-required' of github.com:Ams…
RubenSibon Sep 20, 2024
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
8 changes: 4 additions & 4 deletions packages/css/src/components/blockquote/blockquote.scss
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,17 @@
line-height: var(--ams-blockquote-line-height);
quotes: "“" "”";

@include hyphenation;
alimpens marked this conversation as resolved.
Show resolved Hide resolved
@include text-rendering;
@include reset;

&::before {
content: open-quote;
}

&::after {
content: close-quote;
}

@include hyphenation;
@include text-rendering;
@include reset;
}

.ams-blockquote--inverse-color {
Expand Down
14 changes: 7 additions & 7 deletions packages/css/src/components/button/button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@
padding-inline: var(--ams-button-padding-inline);
touch-action: manipulation;

@include text-rendering;
@include reset-button;

&:disabled,
&[aria-disabled="true"] {
cursor: var(--ams-button-disabled-cursor);
}

@include text-rendering;
@include reset-button;
}

@mixin ams-button-forced-color-mode {
Expand All @@ -46,6 +46,8 @@
box-shadow: var(--ams-button-primary-box-shadow);
color: var(--ams-button-primary-color);

@include ams-button-forced-color-mode;

&:disabled,
[aria-disabled="true"] {
background-color: var(--ams-button-primary-disabled-background-color);
Expand All @@ -56,15 +58,15 @@
background-color: var(--ams-button-primary-hover-background-color);
box-shadow: var(--ams-button-primary-hover-box-shadow);
}

@include ams-button-forced-color-mode;
}

.ams-button--secondary {
background-color: var(--ams-button-secondary-background-color);
box-shadow: var(--ams-button-secondary-box-shadow);
color: var(--ams-button-secondary-color);

@include ams-button-forced-color-mode;

&:disabled,
[aria-disabled="true"] {
background-color: var(--ams-button-secondary-disabled-background-color);
Expand All @@ -76,8 +78,6 @@
box-shadow: var(--ams-button-secondary-hover-box-shadow);
color: var(--ams-button-secondary-hover-color);
}

@include ams-button-forced-color-mode;
}

.ams-button--tertiary {
Expand Down
4 changes: 2 additions & 2 deletions packages/css/src/components/checkbox/checkbox.scss
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
line-height: var(--ams-checkbox-line-height);
outline-offset: var(--ams-checkbox-outline-offset);

@include text-rendering;

&:hover {
color: var(--ams-checkbox-hover-color);
text-decoration-line: underline;
Expand All @@ -55,8 +57,6 @@
border-width: var(--ams-checkbox-checkmark-hover-border-width);
}
}

@include text-rendering;
}

// Default checked
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@
gap: var(--ams-description-list-gap);
line-height: var(--ams-description-list-line-height);

@include reset;
@include text-rendering;

@media screen and (min-width: $ams-breakpoint-medium) {
grid-template-columns: 1fr 2fr;
}

@include reset;
@include text-rendering;
}

.ams-description-list--inverse-color {
Expand All @@ -46,10 +46,10 @@
font-weight: var(--ams-description-list-details-font-weight);
padding-inline-start: var(--ams-description-list-details-padding-inline-start);

@include reset-details;

@media screen and (min-width: $ams-breakpoint-medium) {
grid-column-start: 2;
padding-inline-start: 0;
}

@include reset-details;
}
4 changes: 4 additions & 0 deletions packages/css/src/components/field-set/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ A component to group related form inputs.
## Guidelines

- Use Field Set when you need to show a relationship between multiple form inputs. For example, you may need to group a set of text inputs into a single Field Set when asking for an address.
- If all the child form elements get a `required` attribute with the same value, then add the same property with that value to the Field Set. Also make sure to have an empty string as a value for the `flag` property on all the Label components to avoid many, redundant ‟not required” labels.
- If the various child form elements have mixed `required` attribute values then do the reverse and leave the `required` property undefined and set the `flag` property to an empty string on the Field Set and set the `required` property on the child labels and inputs as needed.
- Make sure to set the `optional` prop to `true` if the associated form elements are not required for form submission. The Field Set's legend will then get the descriptive text “(niet verplicht)” if the `hint` prop is not set.
- If a `hint` is provided its value will be displayed after the legend regardless of the value of `optional`.
RubenSibon marked this conversation as resolved.
Show resolved Hide resolved

## Relevant WCAG requirements

Expand Down
4 changes: 2 additions & 2 deletions packages/css/src/components/file-input/file-input.scss
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,13 @@
padding-block: var(--ams-file-input-file-selector-button-padding-block);
padding-inline: var(--ams-file-input-file-selector-button-padding-inline);

@include reset-button;

@media screen and (-ms-high-contrast: active), screen and (forced-colors: active) {
border: var(
--ams-file-input-file-selector-button-forced-color-mode-border
); // add border because forced colors changes box-shadow to none
}

@include reset-button;
}

.ams-file-input:disabled::file-selector-button {
Expand Down
9 changes: 9 additions & 0 deletions packages/css/src/components/hint/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!-- @license CC0-1.0 -->

# Hint

Provides additional information to an inline element such as a `Label` or `Legend` in a `Field` or `FieldSet` respectively. Typically used to communicate whether a form field is optional or required.
RubenSibon marked this conversation as resolved.
Show resolved Hide resolved

## Class name

`ams-hint`
9 changes: 9 additions & 0 deletions packages/css/src/components/hint/hint.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* @license EUPL-1.2+
* Copyright Gemeente Amsterdam
*/

.ams-hint {
color: var(--ams-hint-color);
white-space: nowrap;
alimpens marked this conversation as resolved.
Show resolved Hide resolved
}
4 changes: 2 additions & 2 deletions packages/css/src/components/icon-button/icon-button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
outline-offset: var(--ams-icon-button-outline-offset);
touch-action: manipulation;

@include reset;

&:hover {
background-color: var(--ams-icon-button-hover-background-color);
color: var(--ams-icon-button-hover-color);
Expand All @@ -30,8 +32,6 @@
color: var(--ams-icon-button-disabled-color);
cursor: not-allowed;
}

@include reset;
}

.ams-icon-button--contrast-color {
Expand Down
1 change: 1 addition & 0 deletions packages/css/src/components/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

/* Append here */
@import "./hint/hint";
@import "./password-input/password-input";
@import "./form-error-list/form-error-list";
@import "./table-of-contents/table-of-contents";
Expand Down
2 changes: 2 additions & 0 deletions packages/css/src/components/label/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ Describes a form control.
## Guidelines

- Always associate a form element (such as an `input` or `textarea`) with a label.
- Make sure to set the `optional` prop to `true` if the associated form element is not required for form submission. The label will then get the descriptive text “(niet verplicht)” if the `hint` prop is not set.
- If a `hint` is provided its value will be displayed after the label regardless of the value of `optional`.
RubenSibon marked this conversation as resolved.
Show resolved Hide resolved
4 changes: 2 additions & 2 deletions packages/css/src/components/link/link.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
font-weight: var(--ams-link-font-weight);
outline-offset: var(--ams-link-outline-offset);

@include text-rendering;

&:hover {
color: var(--ams-link-hover-color);
}

@include text-rendering;
}

.ams-link--standalone {
Expand Down
12 changes: 6 additions & 6 deletions packages/css/src/components/pagination/pagination.scss
Original file line number Diff line number Diff line change
Expand Up @@ -45,22 +45,22 @@
text-underline-offset: var(--ams-pagination-button-text-underline-offset);
touch-action: manipulation;

&:hover {
color: var(--ams-pagination-button-hover-color);
text-decoration-line: var(--ams-pagination-button-hover-text-decoration-line);
}
@include text-rendering;

&:disabled {
display: none;
}

&:hover {
color: var(--ams-pagination-button-hover-color);
text-decoration-line: var(--ams-pagination-button-hover-text-decoration-line);
}

// Override for icon size
span.ams-icon svg {
block-size: 1rem;
inline-size: 1rem;
}

@include text-rendering;
}

.ams-pagination__button--current {
Expand Down
4 changes: 2 additions & 2 deletions packages/css/src/components/radio/radio.scss
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
line-height: var(--ams-radio-line-height);
outline-offset: var(--ams-radio-outline-offset);

@include text-rendering;

&:hover {
color: var(--ams-radio-hover-color);
text-decoration-line: underline;
Expand All @@ -55,8 +57,6 @@
border-color: var(--ams-radio-circle-hover-border-color);
}
}

@include text-rendering;
}

// Default checked
Expand Down
4 changes: 2 additions & 2 deletions packages/css/src/components/select/select.scss
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
padding-inline: var(--ams-select-padding-inline);
touch-action: manipulation;

@include reset;

&:not([multiple]) {
background-image: var(--ams-select-background-image);
background-position: var(--ams-select-background-position);
Expand All @@ -33,8 +35,6 @@
&:hover {
box-shadow: var(--ams-select-hover-box-shadow);
}

@include reset;
}

.ams-select:invalid,
Expand Down
32 changes: 32 additions & 0 deletions packages/react/src/FieldSet/FieldSet.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,36 @@ describe('FieldSet', () => {

expect(ref.current).toBe(component)
})

it("renders a hint text after the field-set's legend not marked as optional", () => {
RubenSibon marked this conversation as resolved.
Show resolved Hide resolved
const { container } = render(<FieldSet legend="Legend" hint="hint text" />)

const legend = container.querySelector('legend')

expect(legend).toHaveTextContent('Legend (hint text)')
RubenSibon marked this conversation as resolved.
Show resolved Hide resolved
})

it('renders the default hint text "niet verplicht" after the field-set\'s legend marked as optional', () => {
alimpens marked this conversation as resolved.
Show resolved Hide resolved
const { container } = render(<FieldSet legend="Legend" optional={true} />)
RubenSibon marked this conversation as resolved.
Show resolved Hide resolved

const legend = container.querySelector('legend')
alimpens marked this conversation as resolved.
Show resolved Hide resolved

expect(legend).toHaveTextContent('Legend (niet verplicht)')
})

it('renders the provided hint text "optional" after the field-set\'s legend marked as optional', () => {
alimpens marked this conversation as resolved.
Show resolved Hide resolved
const { container } = render(<FieldSet legend="Legend" optional={true} hint="optional" />)
alimpens marked this conversation as resolved.
Show resolved Hide resolved

const legend = container.querySelector('legend')

expect(legend).toHaveTextContent('Legend (optional)')
})

it('renders the provided hint text "required" after the field-set\'s legend marked as not optional', () => {
alimpens marked this conversation as resolved.
Show resolved Hide resolved
const { container } = render(<FieldSet legend="Legend" optional={false} hint="required" />)

const legend = container.querySelector('legend')

expect(legend).toHaveTextContent('Legend (required)')
})
})
40 changes: 29 additions & 11 deletions packages/react/src/FieldSet/FieldSet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,43 @@
import clsx from 'clsx'
import { forwardRef } from 'react'
import type { ForwardedRef, HTMLAttributes, PropsWithChildren } from 'react'
import { Hint, HintProps } from '../Hint'

export type FieldSetProps = PropsWithChildren<HTMLAttributes<HTMLFieldSetElement>> & {
/** Whether the field set has an input with a validation error */
invalid?: boolean
/** The text for the caption. */
legend: string
}
/** Whether the associated inputs are optional. Will append the text '(niet verplicht)' to the label if no hint is provided. */
alimpens marked this conversation as resolved.
Show resolved Hide resolved
optional?: boolean
} & HintProps

export const FieldSet = forwardRef(
({ children, className, invalid, legend, ...restProps }: FieldSetProps, ref: ForwardedRef<HTMLFieldSetElement>) => (
<fieldset
{...restProps}
ref={ref}
className={clsx('ams-field-set', invalid && 'ams-field-set--invalid', className)}
>
<legend className="ams-field-set__legend">{legend}</legend>
{children}
</fieldset>
),
(
{ children, className, invalid, legend, hint, optional, ...restProps }: FieldSetProps,
alimpens marked this conversation as resolved.
Show resolved Hide resolved
ref: ForwardedRef<HTMLFieldSetElement>,
) => {
let optionalHint = null

if (optional) {
optionalHint = hint ? <Hint hint={hint} /> : <Hint hint="niet verplicht" />
} else if (hint) {
optionalHint = <Hint hint={hint} />
}
alimpens marked this conversation as resolved.
Show resolved Hide resolved

return (
<fieldset
{...restProps}
ref={ref}
className={clsx('ams-field-set', invalid && 'ams-field-set--invalid', className)}
>
<legend className="ams-field-set__legend">
{legend} {optionalHint}
</legend>
{children}
</fieldset>
)
},
)

FieldSet.displayName = 'FieldSet'
Loading