Skip to content
Draft
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
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ jobs:
uses: chromaui/action@07791f8243f4cb2698bf4d00426baf4b2d1cb7e0 # v13.3.5
if: |
github.event.pull_request.draft == false &&
github.actor != 'dependabot[bot]'
github.actor != 'dependabot[bot]' || (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'ci/chromatic'))
with:
autoAcceptChanges: ${{ env.MAIN_BRANCH }}
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
Expand Down
1 change: 1 addition & 0 deletions components/focus-ring/src/_mixin.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
z-index: 1;
}

/* TODO: remove utrecht-focus with next mayor release */
/* stylelint-disable-next-line block-no-empty */
@mixin utrecht-focus {
}
Expand Down
96 changes: 75 additions & 21 deletions components/textbox/src/_mixin.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@

$utrecht-support-prince-xml: false !default;

/* TODO: Enable ordering properties when the plugin supports logical CSS properties
* https://github.com/hudochenkov/stylelint-order/pull/162 */
/* stylelint-disable order/properties-alphabetical-order */
@mixin utrecht-textbox {
@mixin utrecht-textbox-sizing {
/* The average character inline-size is an approximation, with a default that works for Dutch text.
* The average might need to be configured specifically, for other scripts (CJK characters)
* and very wide or very narrow fonts.
Expand All @@ -33,9 +30,31 @@
var(--utrecht-textbox-padding-inline-end, var(--utrecht-form-control-padding-inline-end, 0)) +
var(--utrecht-textbox-padding-inline-start, var(--utrecht-form-control-padding-inline-start, 0)) +
var(--utrecht-textbox-border-width, var(--utrecht-form-control-border-width, 0)) +
var(--utrecht-textbox-leading-inline-size, 0) + var(--utrecht-textbox-trailing-inline-size, 0) +
var(--utrecht-textbox-autocomplete-ui-size, 44px)
);

inline-size: max(
var(--utrecht-pointer-target-min-size, 44px),
min(
var(--_utrecht-textbox-max-inline-size, 100cqi),
var(--utrecht-textbox-max-inline-size, var(--utrecht-form-control-max-inline-size))
)
);

/* min-inline-size: var(--utrecht-pointer-target-min-size, 44px);
max-inline-size: min(
var(--_utrecht-textbox-max-inline-size, 100%),
var(--utrecht-textbox-max-inline-size, var(--utrecht-form-control-max-inline-size))
); */

Check warning on line 49 in components/textbox/src/_mixin.scss

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this commented out code.

See more on https://sonarcloud.io/project/issues?id=nl-design-system_utrecht&issues=AZ2b8Uopg--gWPrDP4Aw&open=AZ2b8Uopg--gWPrDP4Aw&pullRequest=3112
}

/* TODO: Enable ordering properties when the plugin supports logical CSS properties
* https://github.com/hudochenkov/stylelint-order/pull/162 */
/* stylelint-disable order/properties-alphabetical-order */
@mixin utrecht-textbox {
@include utrecht-textbox-sizing;

background-color: var(--utrecht-textbox-background-color, var(--utrecht-form-control-background-color));
block-size: initial; /* harden */
border-width: var(--utrecht-textbox-border-width, var(--utrecht-form-control-border-width));
Expand All @@ -56,24 +75,19 @@
var(--utrecht-form-control-font-weight, initial)
); /* harden with `initial` */

inline-size: 100%;
/* inline-size: 100%; */

Check warning on line 78 in components/textbox/src/_mixin.scss

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this commented out code.

See more on https://sonarcloud.io/project/issues?id=nl-design-system_utrecht&issues=AZ2b8Uopg--gWPrDP4Ax&open=AZ2b8Uopg--gWPrDP4Ax&pullRequest=3112
line-height: var(--utrecht-textbox-line-height, var(--utrecht-form-control-line-height, initial));
min-block-size: var(--utrecht-pointer-target-min-size, 44px);
min-inline-size: var(--utrecht-pointer-target-min-size, 44px);
max-inline-size: min(
var(--_utrecht-textbox-max-inline-size, 100%),
var(--utrecht-textbox-max-inline-size, var(--utrecht-form-control-max-inline-size))
);
overflow: hidden;
padding-block-end: var(--utrecht-textbox-padding-block-end, var(--utrecht-form-control-padding-block-end, 0));
padding-block-start: var(--utrecht-textbox-padding-block-start, var(--utrecht-form-control-padding-block-start, 0));
padding-inline-end: var(
--utrecht-textbox-padding-inline-end,
var(--utrecht-form-control-padding-inline-end, initial)
padding-inline-start: calc(
var(--utrecht-textbox-padding-inline-start, var(--utrecht-form-control-padding-inline-start, initial)) +
var(--utrecht-textbox-leading-inline-size, 0px)
);
padding-inline-start: var(
--utrecht-textbox-padding-inline-start,
var(--utrecht-form-control-padding-inline-start, initial)
padding-inline-end: calc(
var(--utrecht-textbox-padding-inline-end, var(--utrecht-form-control-padding-inline-end, initial)) +
var(--utrecht-textbox-trailing-inline-size, 0px)
);
white-space: nowrap;

Expand Down Expand Up @@ -150,8 +164,13 @@
cursor: var(--utrecht-action-disabled-cursor, not-allowed);
}

/* TODO: remove utrecht-textbox--focus with next mayor release */
/* stylelint-disable-next-line block-no-empty */
@mixin utrecht-textbox--focus {
@include utrecht-focus;
}

@mixin utrecht-textbox--focus-visible {
@include utrecht-focus-visible;

background-color: var(
--utrecht-textbox-focus-background-color,
Expand All @@ -173,10 +192,6 @@
);
}

@mixin utrecht-textbox--focus-visible {
@include utrecht-focus-visible;
}

@mixin utrecht-textbox--read-only {
background-color: var(
--utrecht-textbox-read-only-background-color,
Expand Down Expand Up @@ -405,3 +420,42 @@
/* Dutch IBAN: 18 characters, plus 4 spaces (one space after every 4 characters) */
--utrecht-textbox-value-max-length: 22;
}

/**
* New textbox container
*/
@mixin utrecht-textbox-container__inner {
/* @include utrecht-textbox-sizing; */

Check warning on line 428 in components/textbox/src/_mixin.scss

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this commented out code.

See more on https://sonarcloud.io/project/issues?id=nl-design-system_utrecht&issues=AZ2b8Uopg--gWPrDP4Ay&open=AZ2b8Uopg--gWPrDP4Ay&pullRequest=3112

--utrecht-icon-size: 1lh;
--utrecht-textbox-container-slot-min-inline-size: var(--utrecht-pointer-target-min-size, 44px);
--utrecht-textbox-leading-inline-size: max(
var(--_utrecht-textbox-leading-inline-size-calculated, 0px),
var(--utrecht-textbox-container-slot-min-inline-size)
);
--utrecht-textbox-trailing-inline-size: max(
var(--_utrecht-textbox-trailing-inline-size-calculated, 0px),
var(--utrecht-textbox-container-slot-min-inline-size)
);

&:where(:not(:has(.utrecht-textbox-container__leading))) {
--utrecht-textbox-leading-inline-size: 0px;
}
&:where(:not(:has(.utrecht-textbox-container__trailing))) {
--utrecht-textbox-trailing-inline-size: 0px;
}

display: inline-grid;
grid-template-columns: max-content 1fr max-content;
gap: var(
--utrecht-textbox-gap,
var(--utrecht-textbox-padding-inline-start, var(--utrecht-form-control-padding-inline-start, initial))
);
block-size: initial; /* harden */
box-sizing: border-box;
inline-size: fit-content;
}

@mixin utrecht-textbox-container__input {
grid-area: 1 / 1 / 2 / 4;
}
46 changes: 46 additions & 0 deletions components/textbox/src/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,49 @@
@include utrecht-textbox--numeric;
}
}

/*
* Textbox container
*/
.utrecht-textbox-sizing {
@include utrecht-textbox-sizing;
}

.utrecht-textbox-container {
background-color: rgb(0 255 0 / 20%);
container: utrecht-textbox-container / inline-size;
display: block;
}

.utrecht-textbox-container__inner {
@include utrecht-textbox-container__inner;
}

.utrecht-textbox-container__leading,
.utrecht-textbox-container__trailing {
align-items: center;
background-color: rgb(255 0 0 / 20%);
display: flex;
justify-content: center;
min-inline-size: var(--utrecht-textbox-container-slot-min-inline-size, 44px);
position: relative;
white-space: nowrap;
z-index: 2;

// TODO: update list of interactive elements
&:not(:has(button, a)) {
pointer-events: none;
}
}

.utrecht-textbox-container__leading {
grid-area: 1 / 1 / 2 / 2;
}

.utrecht-textbox-container__trailing {
grid-area: 1 / 3 / 2 / 4;
}

.utrecht-textbox-container__input {
@include utrecht-textbox-container__input;
}
166 changes: 165 additions & 1 deletion packages/components-react/textbox-react/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
import clsx from 'clsx';
import { ForwardedRef, forwardRef, InputHTMLAttributes } from 'react';
import {
CSSProperties,
ForwardedRef,
forwardRef,
HTMLAttributes,
InputHTMLAttributes,
ReactNode,
useEffect,
useImperativeHandle,
useRef,
useState,
} from 'react';
export type TextboxTypes =
| 'date'
| 'datetime-local'
Expand Down Expand Up @@ -61,3 +72,156 @@
);

Textbox.displayName = 'Textbox';

/**
* TextboxContainer
* Wrapper around a textbox input with optional leading and trailing elements.
*/
interface TextboxContainerOwnProps {
leading?: ReactNode;
trailing?: ReactNode;
}

export interface TextboxContainerProps extends HTMLAttributes<HTMLSpanElement>, TextboxContainerOwnProps {}

export const TextboxContainer = forwardRef<HTMLSpanElement, TextboxContainerProps>(
({ children, className, leading, trailing, ...restProps }: TextboxContainerProps, ref) => {
const leadingRef = useRef<HTMLSpanElement>(null);
const trailingRef = useRef<HTMLSpanElement>(null);
const containerInnerRef = useRef<HTMLSpanElement>(null);
const resizeTimeoutRef = useRef<number | null>(null);
const [leadingInlineSize, setLeadingInlineSize] = useState<string>('0px');
const [trailingInlineSize, setTrailingInlineSize] = useState<string>('0px');

const updateInlineSizes = () => {
if (leadingRef.current) {
const width = leadingRef.current.getBoundingClientRect().width;
setLeadingInlineSize(`${width}px`);
}
if (trailingRef.current) {
const width = trailingRef.current.getBoundingClientRect().width;
setTrailingInlineSize(`${width}px`);
}
};

const deBouncedOnResize = () => {
if (resizeTimeoutRef.current) {
clearTimeout(resizeTimeoutRef.current);
}
resizeTimeoutRef.current = window?.setTimeout(updateInlineSizes, 100);

Check warning on line 111 in packages/components-react/textbox-react/src/index.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `globalThis` over `window`.

See more on https://sonarcloud.io/project/issues?id=nl-design-system_utrecht&issues=AZ2WSGMp2qvbif5B0iPS&open=AZ2WSGMp2qvbif5B0iPS&pullRequest=3112
};

useEffect(() => {
updateInlineSizes();

const resizeObserver = new ResizeObserver(deBouncedOnResize);

if (containerInnerRef.current) {
resizeObserver.observe(containerInnerRef.current);
}

// Cleanup
return () => {
resizeObserver.disconnect();
if (resizeTimeoutRef.current) {
clearTimeout(resizeTimeoutRef.current);
}
};
}, [leading, trailing]);

return (
<span
{...restProps}
className={clsx('utrecht-textbox-container', className)}
style={{
['--_utrecht-textbox-leading-inline-size-calculated' as keyof CSSProperties]: leadingInlineSize,
['--_utrecht-textbox-trailing-inline-size-calculated' as keyof CSSProperties]: trailingInlineSize,
}}
ref={ref}
>
<span className="utrecht-textbox-container__inner" ref={containerInnerRef}>
{leading ? (
<span ref={leadingRef} className="utrecht-textbox-container__leading">
{leading}
</span>
) : null}
{children}
{trailing ? (
<span ref={trailingRef} className="utrecht-textbox-container__trailing">
{trailing}
</span>
) : null}
</span>
</span>
);
},
);

TextboxContainer.displayName = 'TextboxContainer';

/**
* Textbox2
*/
export interface Textbox2Props extends InputHTMLAttributes<HTMLInputElement>, TextboxContainerOwnProps {
inputRequired?: boolean;
invalid?: boolean;
type?: string | TextboxTypes;

Check warning on line 168 in packages/components-react/textbox-react/src/index.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

"number" | "date" | "datetime-local" | "email" | "month" | "password" | "search" | "tel" | "text" | "time" | "url" | "week" is overridden by string in this union type.

See more on https://sonarcloud.io/project/issues?id=nl-design-system_utrecht&issues=AZ2MPDSvBWYYs-d77wDE&open=AZ2MPDSvBWYYs-d77wDE&pullRequest=3112
}

export const Textbox2 = forwardRef<HTMLInputElement, Textbox2Props>(
({ className, leading, trailing, ...restProps }: Textbox2Props, ref) => {
const inputRef = useRef<HTMLInputElement>(null);
const containerInnerRef = useRef<HTMLSpanElement>(null);

// Default ref behavior: point to the input element
useImperativeHandle(ref, () => inputRef.current!, []);

const renderContainer = leading || trailing;

// const inputClasses = clsx(
// 'utrecht-textbox',
// 'utrecht-textbox--html-input',
// disabled && 'utrecht-textbox--disabled',
// invalid && 'utrecht-textbox--invalid',
// readOnly && 'utrecht-textbox--readonly',
// (required || inputRequired) && 'utrecht-textbox--required',
// className,
// );

Check warning on line 189 in packages/components-react/textbox-react/src/index.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this commented out code.

See more on https://sonarcloud.io/project/issues?id=nl-design-system_utrecht&issues=AZ2b8Ukug--gWPrDP4At&open=AZ2b8Ukug--gWPrDP4At&pullRequest=3112

// const inputClassesInContainer = clsx(
// 'utrecht-textbox-container__input',
// (required || inputRequired) && 'utrecht-textbox-container__input--required',
// );

Check warning on line 194 in packages/components-react/textbox-react/src/index.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this commented out code.

See more on https://sonarcloud.io/project/issues?id=nl-design-system_utrecht&issues=AZ2b8Ukug--gWPrDP4Au&open=AZ2b8Ukug--gWPrDP4Au&pullRequest=3112

const inputClasses = clsx('utrecht-textbox-sizing', className);
const inputClassesInContainer = clsx('utrecht-textbox-container__input', className);

// const Input = (
// <input
// {...restProps}
// ref={inputRef}
// type={type}
// className={renderContainer ? inputClassesInContainer : inputClasses}
// dir={dir ?? 'auto'}
// disabled={disabled}
// readOnly={readOnly}
// aria-required={required ? required : undefined}
// required={inputRequired}
// aria-invalid={invalid || undefined}
// inputMode={inputMode || (type === 'number' ? 'numeric' : undefined)}
// />
// );

Check warning on line 213 in packages/components-react/textbox-react/src/index.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this commented out code.

See more on https://sonarcloud.io/project/issues?id=nl-design-system_utrecht&issues=AZ2b8Ukug--gWPrDP4Av&open=AZ2b8Ukug--gWPrDP4Av&pullRequest=3112

if (renderContainer) {
return (
<TextboxContainer ref={containerInnerRef} leading={leading} trailing={trailing}>
<Textbox {...restProps} className={inputClassesInContainer} ref={inputRef} />
</TextboxContainer>
);
}

return <Textbox {...restProps} className={inputClasses} ref={inputRef} />;
},
);

Textbox2.displayName = 'Textbox2';
1 change: 1 addition & 0 deletions packages/storybook-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@
"@utrecht/table-css": "workspace:*",
"@utrecht/textarea-css": "workspace:*",
"@utrecht/textbox-css": "workspace:*",
"@utrecht/textbox-react": "workspace:*",
"@utrecht/tooltip-css": "workspace:*",
"@utrecht/tooltip-react": "workspace:*",
"@utrecht/unordered-list-css": "workspace:*",
Expand Down
Loading
Loading