Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/loud-shoes-promise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@storefront-ui/react': major
---

- **[BREAKING][CHANGED]** upgrade to React 19. There are no breaking changes specific to SFUI library itself. To upgrade, please [follow official React guide](https://react.dev/blog/2024/04/25/react-19-upgrade-guide).
16 changes: 16 additions & 0 deletions .cursor/commands/create-pr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Open a PR

- Check that I'm in a branch other than `main`, `staging` or `dev`. If not, bail and explain.
- DO NOT create a new branch, use existing one
- Check the diff between my branch and the base branch (usually `dev`)
- If there's unstaged or staged work that hasn't been commited, commit all the relevant code first
(Use `gh` in case it's installed)
- Write up a quick PR description in the following format
<TLDR> (no more than 2 sentences)

<Description>
- 1~3 bullet points explaining what's changing

- Always use semantic convention as github title. For instance `docs(AT-737): multiple cms-es guide`, `feat(AT-712): add typedApiClient`. Ticket number should be present in the branch name, if not - skip it
- Always paste the link to the PR in your response so I can click it easily
- Prepend GIT_EDITOR=true to all git commands you run, so you can avoid getting blocked as you execute commands
3 changes: 2 additions & 1 deletion _templates/component/new/react_component.ejs.t
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
to: packages/sfui/frameworks/react/components/<%= name %>/<%= name %>.tsx
force: false
---
import React from 'react';
<% Props = name + 'Props' %>import type { <%= Props %> } from './types';

export default function <%= name %>({
...attributes
}: <%= Props %>): JSX.Element {
}: <%= Props %>): React.JSX.Element {
return (

);
Expand Down
4 changes: 2 additions & 2 deletions apps/preview/next/components/utils/Controls.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import classNames from 'classnames';
import { ChangeEvent, useState } from 'react';
import React, { ChangeEvent, useState } from 'react';
import { SfButton, SfButtonVariant, SfButtonSize, SfIconExpandLess, SfIconExpandMore } from '@storefront-ui/react';
import { useControlsSearchParams } from '../../composables/utils/useControlsSearchParams';
import { ControlOptionBind, ControlsProps, ControlsType } from './types';
Expand Down Expand Up @@ -100,7 +100,7 @@ export default function Controls<T extends { [k: string]: any }>({ controls, sta
<span id={control.modelName}>{control.modelName}</span>
</td>
<td className="value">
{((): JSX.Element | JSX.Element[] => {
{((): React.JSX.Element | React.JSX.Element[] => {
switch (control.type) {
case 'select':
return (
Expand Down
8 changes: 4 additions & 4 deletions apps/preview/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
"classnames": "^2.5.1",
"lodash-es": "^4.17.21",
"next": "^14.2.32",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-transition-group": "^4.4.5",
"react-use": "^17.5.1",
"swr": "^2.2.5"
Expand All @@ -36,8 +36,8 @@
"@tailwindcss/typography": "^0.5.19",
"@types/lodash-es": "^4.17.12",
"@types/node": "^20.12.7",
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@types/react-transition-group": "^4.4.5",
"glob-promise": "^6.0.7",
"prettier": "^3.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ const options = [
export default function DrawerWithTransition() {
const [open, setOpen] = useState(false);
const [placement, setPlacement] = useState<`${SfDrawerPlacement}`>('left');
const nodeRef = useRef(null);
const drawerRef = useRef(null);
const nodeRef = useRef<Transition<HTMLElement | undefined>>(null);
const drawerRef = useRef<HTMLElement>(null);

useTrapFocus(drawerRef, { activeState: open });

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { ShowcasePageLayout } from '../../showcases';

// #region source
import { useRef, useState } from 'react';
import { type RefObject, useRef, useState } from 'react';
import { useIntersection } from 'react-use';
import {
SfScrollable,
Expand Down Expand Up @@ -36,13 +36,13 @@ export default function GalleryVertical() {
const firstThumbRef = useRef<HTMLButtonElement>(null);
const [activeIndex, setActiveIndex] = useState(0);

const firstThumbVisible = useIntersection(firstThumbRef, {
const firstThumbVisible = useIntersection(firstThumbRef as RefObject<HTMLElement>, {
root: thumbsRef.current,
rootMargin: '0px',
threshold: 1,
});

const lastThumbVisible = useIntersection(lastThumbRef, {
const lastThumbVisible = useIntersection(lastThumbRef as RefObject<HTMLElement>, {
root: thumbsRef.current,
rootMargin: '0px',
threshold: 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ export default function MegaMenuNavigation() {
const [inputValue, setInputValue] = useState('');

const refsByKey = useMemo(() => {
const buttonRefs: Record<string, RefObject<HTMLButtonElement>> = {};
const buttonRefs: Record<string, RefObject<HTMLButtonElement | null>> = {};
content.children?.forEach((item) => {
buttonRefs[item.key] = createRef();
});
Expand Down
8 changes: 2 additions & 6 deletions apps/preview/next/pages/showcases/ProductSlider/Basic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const products = Array.from(Array(10), (_, i) => ({
},
}));

function ButtonPrev({ disabled, ...attributes }: { disabled?: boolean }) {
function ButtonPrev({ disabled = false, ...attributes }: { disabled?: boolean }) {
return (
<SfButton
className={classNames('absolute !rounded-full z-10 left-4 bg-white hidden md:block', {
Expand All @@ -39,9 +39,7 @@ function ButtonPrev({ disabled, ...attributes }: { disabled?: boolean }) {
);
}

ButtonPrev.defaultProps = { disabled: false };

function ButtonNext({ disabled, ...attributes }: { disabled?: boolean }) {
function ButtonNext({ disabled = false, ...attributes }: { disabled?: boolean }) {
return (
<SfButton
className={classNames('absolute !rounded-full z-10 right-4 bg-white hidden md:block', {
Expand All @@ -57,8 +55,6 @@ function ButtonNext({ disabled, ...attributes }: { disabled?: boolean }) {
);
}

ButtonNext.defaultProps = { disabled: false };

export default function ProductSliderBasic() {
return (
<SfScrollable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/* eslint-disable no-console */
import { ShowcasePageLayout } from '../../showcases';
// #region source
import { type ChangeEvent, type FormEvent, type KeyboardEvent, useState, useRef } from 'react';
import React, { type ChangeEvent, type FormEvent, type KeyboardEvent, useState, useRef } from 'react';
import { useDebounce } from 'react-use';
import { offset } from '@floating-ui/react-dom';
import {
Expand All @@ -21,7 +21,7 @@ import {
interface Product {
id: string;
name: string;
thumbnail?: JSX.Element;
thumbnail?: React.JSX.Element;
image?: string;
}

Expand Down
8 changes: 4 additions & 4 deletions apps/test/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
"test:ci:react": "yarn test:ci && CYPRESS_COVERAGE=true yarn cypress run --component && yarn generate-coverage"
},
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1"
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@cypress/code-coverage": "3.13.4",
Expand All @@ -37,8 +37,8 @@
"@storefront-ui/typography": "workspace:*",
"@tailwindcss/postcss": "^4.1.14",
"@tailwindcss/typography": "^0.5.19",
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@vitejs/plugin-react": "^4.3.1",
"autoprefixer": "^10.4.19",
"chokidar-cli": "^3.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import React from 'react';
import { SfBadgePlacement } from '@storefront-ui/shared';
import { twMerge, type SfBadgeProps } from '@storefront-ui/react';

Expand All @@ -8,7 +9,7 @@ export default function SfBadge({
placement = SfBadgePlacement['top-right'],
className,
...attributes
}: SfBadgeProps): JSX.Element {
}: SfBadgeProps): React.JSX.Element {
const isDot = variant === 'dot';
let displayValue = content;
if (isDot) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useRef, useEffect, useState } from 'react';
import React, { useRef, useEffect, useState } from 'react';
import {
mergeRefs,
SfIconCheckBox,
Expand All @@ -15,7 +15,7 @@ const SfCheckbox = polymorphicForwardRef<'input', SfCheckboxProps>(
(
{ wrapperAs, invalid, className, indeterminate: indeterminateProp, wrapperClassName, ...attributes },
ref,
): JSX.Element => {
): React.JSX.Element => {
const inputRef = useRef<HTMLInputElement>(null);
const WrapperTag = wrapperAs || defaultWrapperTag;
const [isIndeterminate, setIsIndeterminate] = useState(indeterminateProp || false);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import React from 'react';
import type { SfCounterProps } from '@storefront-ui/react';
import { SfCounterSize, twMerge } from '@storefront-ui/react';

Expand All @@ -7,7 +8,7 @@ export default function SfCounter({
children,
className,
...attributes
}: SfCounterProps): JSX.Element {
}: SfCounterProps): React.JSX.Element {
const sizeClasses: Record<SfCounterSize, string> = {
[SfCounterSize['3xs']]: twMerge('text-3xs', { 'px-1': pill }),
[SfCounterSize['2xs']]: twMerge('text-2xs', { 'px-1.5': pill }),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import React from 'react';
import { SfLoaderSize, twMerge } from '@storefront-ui/react';
import type { SfLoaderCircularProps } from '@storefront-ui/react';

Expand Down Expand Up @@ -29,7 +30,7 @@ export default function SfLoaderCircular({
className,
circleClassName,
...attributes
}: SfLoaderCircularProps): JSX.Element {
}: SfLoaderCircularProps): React.JSX.Element {
return (
<svg
className={twMerge(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import React from 'react';
import { SfLoaderLinearSize, SfLoaderSize, twMerge } from '@storefront-ui/react';
import type { SfLoaderLinearProps } from '@storefront-ui/react';

Expand All @@ -18,7 +19,7 @@ export default function SfLoaderLinear({
ariaLabel = 'loading',
className,
...attributes
}: SfLoaderLinearProps): JSX.Element {
}: SfLoaderLinearProps): React.JSX.Element {
return (
<span
className={twMerge(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import React from 'react';
import { SfProgressSize, twMerge } from '@storefront-ui/react';
import type { SfProgressCircularProps } from '@storefront-ui/react';

Expand Down Expand Up @@ -31,7 +32,7 @@ export default function SfProgressCircular({
className,
circleClassName,
...attributes
}: SfProgressCircularProps): JSX.Element {
}: SfProgressCircularProps): React.JSX.Element {
const strokeDasharray = `${(value / 100) * 151}, 150`;
return (
<svg
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import React from 'react';
import { SfProgressSize, SfProgressLinearSize, twMerge } from '@storefront-ui/react';
import type { SfProgressLinearProps } from '@storefront-ui/react';

Expand All @@ -19,7 +20,7 @@ export default function SfProgressLinear({
ariaLabel = 'Progress element',
className,
...attributes
}: SfProgressLinearProps): JSX.Element {
}: SfProgressLinearProps): React.JSX.Element {
return (
<progress
data-testid="progress-linear"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'use client';
import { useState, type ChangeEvent } from 'react';
import React, { useState, type ChangeEvent } from 'react';
import classnames from 'classnames';
import {
SfIconStar,
Expand Down Expand Up @@ -50,7 +50,7 @@ export default function SfRatingButton({
getLabelText = defaultLabelText,
children = renderDefaultIcon,
...attributes
}: SfRatingButtonProps): JSX.Element {
}: SfRatingButtonProps): React.JSX.Element {
const [hoverValue, setHoverValue] = useState(0);
const icons = Array.from({ length: Math.floor(Math.abs(max)) }, (_, index) => index + 1);
const isIconFilled = (ratingValue: number) => ratingValue <= hoverValue || (hoverValue === 0 && ratingValue <= value);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { forwardRef } from 'react';
import React, { forwardRef } from 'react';
import { twMerge, type SfSwitchProps } from '@storefront-ui/react';

const SfSwitch = forwardRef<HTMLInputElement, SfSwitchProps>(
({ invalid, className, ...attributes }, ref): JSX.Element => (
({ invalid, className, ...attributes }, ref): React.JSX.Element => (
<input
ref={ref}
className={twMerge(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';
import { SfTextareaSize, twMerge, useFocusVisible } from '@storefront-ui/react';
import { forwardRef } from 'react';
import React, { forwardRef } from 'react';
import type { SfTextareaProps } from './types';

const sizeClasses = {
Expand All @@ -10,7 +10,7 @@ const sizeClasses = {
};

export default forwardRef<HTMLTextAreaElement, SfTextareaProps>(
({ size = SfTextareaSize.base, invalid = false, className, ...attributes }, ref): JSX.Element => {
({ size = SfTextareaSize.base, invalid = false, className, ...attributes }, ref): React.JSX.Element => {
const { isFocusVisible } = useFocusVisible({ isTextInput: true });

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import React from 'react';
import { SfThumbnailProps, SfThumbnailSize, twMerge } from '@storefront-ui/react';

const sizeClasses = {
Expand All @@ -12,7 +13,7 @@ export default function SfThumbnail({
children,
className,
...attributes
}: SfThumbnailProps): JSX.Element {
}: SfThumbnailProps): React.JSX.Element {
return (
<div
className={twMerge('rounded-full overflow-hidden bg-clip-content p-0.5', sizeClasses[size], className)}
Expand Down
2 changes: 1 addition & 1 deletion packages/sfui/frameworks/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"vite-tsconfig-paths": "^4.3.2"
},
"peerDependencies": {
"react": "^18.3.1"
"react": "^19.0.0"
},
"publishConfig": {
"access": "public"
Expand Down
4 changes: 2 additions & 2 deletions packages/tests/components/SfDropdown/SfDropdown.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,10 @@ describe('SfDropdown', () => {

describe('When click away', () => {
it('Should call onClose', () => {
const props = { onClose: cy.spy() };
const props = { modelValue: ref(true), onClose: cy.spy() };
initializeComponent(props);

page().clickAway(props.onClose);
page().isDropdownVisible().clickAway(props.onClose);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { type SfRatingButtonProps } from '@storefront-ui/react';
import { mount, useComponent } from '../../utils/mount';

const { vue: SfRatingButtonVue, react: SfRatingButtonReact } = useComponent('SfRatingButton');
const { react: SfIconMoreHorizReact } = useComponent('SfIconMoreHoriz');
import SfRatingButtonBaseObject from './SfRatingButton.PageObject';
import { isReact } from '../../utils/utils';

Expand Down Expand Up @@ -133,8 +134,9 @@ describe('SfRatingButton', () => {
describe('when custom icon', () => {
if (isReact) {
it('should pass props to render function', () => {
const props = { children: cy.spy(), max: 5, size: SfRatingButtonSize.base };
const props = { children: cy.spy(() => <SfIconMoreHorizReact />), max: 5, size: SfRatingButtonSize.base };
initializeComponent(props);
page().isVisible();

cy.then(() => {
expect(props.children).to.be.callCount(props.max);
Expand Down
3 changes: 2 additions & 1 deletion packages/tests/utils/fake-import.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// FILE used for vue/react tests, instead of copy and import each react/vue component, we fake it
import React from 'react';

export default function SomeFakeComponent({}): JSX.Element {
export default function SomeFakeComponent({}): React.JSX.Element {
return '';
}
Loading
Loading