Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import { toast } from 'react-hot-toast';

import { FragmentOf } from '~/client/graphql';
import { Field, FieldLabel, Form, FormSubmit } from '~/components/ui/form';
import { Label } from '~/components/ui/label';
import { Message } from '~/components/ui/message';
import { RadioGroup, RadioItem } from '~/components/ui/radio-group';
import { RadioGroup } from '~/components/ui/radio-group';

import { ShippingOptionsFragment } from './fragment';
import { SubmitButton } from './submit-button';
Expand Down Expand Up @@ -42,41 +41,29 @@ export const ShippingOptions = ({ data, checkoutEntityId, currencyCode }: Props)
}
};

return shippingOptions && shippingOptions.length > 0 ? (
const items = shippingOptions?.map((option) => ({
value: option.shippingOptionEntityId,
label: (
<p className="inline-flex w-full justify-between">
{option.description ? <span>{option.description}</span> : null}
<span>{format.number(option.cost, { style: 'currency', currency: currencyCode })}</span>
</p>
),
}));

const defaultValue = shippingOptions?.find((option) => option.isDefault)?.shippingOptionEntityId;

return items && items.length > 0 ? (
<Form action={onSubmit} className="mx-auto mb-4 mt-4 grid w-full grid-cols-1 gap-y-4">
<Field className="relative space-y-2" id="shipping-option" name="option">
<FieldLabel htmlFor="shipping-option">{t('shippingOptions')}</FieldLabel>
<RadioGroup
aria-label={t('radioGroupAriaLabel')}
defaultValue={shippingOptions.find((option) => option.isDefault)?.shippingOptionEntityId}
defaultValue={defaultValue}
items={items}
name="shippingOption"
required={true}
>
{shippingOptions.map((option) => {
return (
<div className="mb-2 flex" key={option.shippingOptionEntityId}>
<RadioItem
id={option.shippingOptionEntityId}
value={option.shippingOptionEntityId}
/>
<Label
className="w-full cursor-pointer ps-4 font-normal"
htmlFor={option.shippingOptionEntityId}
>
<p className="inline-flex w-full justify-between">
<span>{option.description}</span>
<span>
{format.number(option.cost, {
style: 'currency',
currency: currencyCode,
})}
</span>
</p>
</Label>
</div>
);
})}
</RadioGroup>
/>
</Field>
<FormSubmit asChild>
<SubmitButton />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { FragmentOf } from '~/client/graphql';
import { BcImage } from '~/components/bc-image';
import { Label } from '~/components/ui/label';
import { PickList, PickListItem } from '~/components/ui/pick-list';
import { RadioGroup, RadioItem } from '~/components/ui/radio-group';
import { RadioGroup } from '~/components/ui/radio-group';
import { RectangleList, RectangleListItem } from '~/components/ui/rectangle-list';
import { Select, SelectContent, SelectItem } from '~/components/ui/select';
import { Swatch, SwatchItem } from '~/components/ui/swatch';
Expand Down Expand Up @@ -161,6 +161,10 @@ export const MultipleChoiceField = ({ option }: Props) => {
</Label>
<RadioGroup
aria-labelledby={`label-${option.entityId}`}
items={values.map((value) => ({
label: value.label,
value: value.entityId.toString(),
}))}
name={field.name}
onValueChange={(value) => {
field.onChange(value);
Expand All @@ -171,26 +175,7 @@ export const MultipleChoiceField = ({ option }: Props) => {
});
}}
value={field.value?.toString()}
>
{values.map((value) => (
<div className="mb-2 flex" key={value.entityId}>
<RadioItem
id={`${value.entityId}`}
onMouseEnter={() => {
handleMouseEnter({
optionId: option.entityId,
valueId: Number(value.entityId),
});
}}
value={`${value.entityId}`}
variant={error ? 'error' : undefined}
/>
<Label className="cursor-pointer ps-4 font-normal" htmlFor={`${value.entityId}`}>
{value.label}
</Label>
</div>
))}
</RadioGroup>
/>
{error && <ErrorMessage>{error.message}</ErrorMessage>}
</div>
);
Expand Down
94 changes: 29 additions & 65 deletions core/components/ui/radio-group/radio-group.tsx
Original file line number Diff line number Diff line change
@@ -1,74 +1,38 @@
import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
import { cva } from 'class-variance-authority';
import { ComponentPropsWithRef, ElementRef, forwardRef } from 'react';
import { ComponentPropsWithoutRef, ReactNode, useId } from 'react';

import { cn } from '~/lib/utils';

type RadioIndicatorType = typeof RadioGroupPrimitive.Indicator;
interface Props extends ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root> {
error?: boolean;
items: Array<{ value: string; label: ReactNode }>;
}

const radioGroupVariants = cva(
'flex h-6 w-6 items-center justify-center rounded-full border-2 border-gray-200 hover:border-secondary focus-visible:border-primary focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-primary/20 focus-visible:hover:border-secondary radix-state-checked:border-primary radix-state-checked:bg-primary radix-state-checked:hover:border-secondary radix-state-checked:hover:bg-secondary disabled:pointer-events-none disabled:bg-gray-100 radix-state-checked:disabled:border-gray-400 radix-state-checked:disabled:bg-gray-400',
{
variants: {
variant: {
error:
'border-error-secondary focus-visible:border-error-secondary focus-visible:ring-error-secondary/20 hover:border-error focus-visible:hover:border-error disabled:border-gray-200 radix-state-checked:border-error-secondary radix-state-checked:bg-error-secondary radix-state-checked:hover:border-error radix-state-checked:hover:bg-error',
},
},
},
);
const RadioGroup = ({ children, className, error, items, ...props }: Props) => {
const id = useId();

const RadioIndicator = forwardRef<
ElementRef<RadioIndicatorType>,
ComponentPropsWithRef<RadioIndicatorType>
>(({ children, className, ...props }, ref) => {
return (
<RadioGroupPrimitive.Indicator
className={cn('h-2 w-2 rounded-full bg-white', className)}
{...props}
ref={ref}
>
{children}
</RadioGroupPrimitive.Indicator>
<RadioGroupPrimitive.Root className={className} {...props}>
{items.map((item) => (
<div className="mb-2 flex w-full gap-4" key={item.value}>
<RadioGroupPrimitive.Item
className={cn(
'flex h-6 w-6 items-center justify-center rounded-full border-2 border-gray-200 hover:border-secondary focus-visible:border-primary focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-primary/20 focus-visible:hover:border-secondary disabled:pointer-events-none disabled:bg-gray-100 radix-state-checked:border-primary radix-state-checked:bg-primary radix-state-checked:hover:border-secondary radix-state-checked:hover:bg-secondary radix-state-checked:disabled:border-gray-400 radix-state-checked:disabled:bg-gray-400',
error &&
'border-error-secondary hover:border-error focus-visible:border-error-secondary focus-visible:ring-error-secondary/20 focus-visible:hover:border-error disabled:border-gray-200 radix-state-checked:border-error-secondary radix-state-checked:bg-error-secondary radix-state-checked:hover:border-error radix-state-checked:hover:bg-error',
)}
id={`${id}-${item.value}`}
value={item.value}
>
<RadioGroupPrimitive.Indicator className="h-2 w-2 rounded-full bg-white" />
</RadioGroupPrimitive.Item>
<label className="w-full" htmlFor={`${id}-${item.value}`}>
{item.label}
</label>
</div>
))}
</RadioGroupPrimitive.Root>
);
});

RadioIndicator.displayName = 'RadioIndicator';

type RadioItemType = typeof RadioGroupPrimitive.Item;

export interface RadioItemProps extends ComponentPropsWithRef<RadioItemType> {
variant?: 'error';
}

const RadioItem = forwardRef<ElementRef<RadioItemType>, RadioItemProps>(
({ children, className, variant, ...props }, ref) => {
return (
<RadioGroupPrimitive.Item
className={cn(radioGroupVariants({ variant, className }))}
ref={ref}
{...props}
>
{children || <RadioIndicator />}
</RadioGroupPrimitive.Item>
);
},
);

RadioItem.displayName = 'RadioItem';

type RadioGroupType = typeof RadioGroupPrimitive.Root;

const RadioGroup = forwardRef<ElementRef<RadioGroupType>, ComponentPropsWithRef<RadioGroupType>>(
({ children, className, ...props }, ref) => {
return (
<RadioGroupPrimitive.Root className={cn(className)} ref={ref} {...props}>
{children}
</RadioGroupPrimitive.Root>
);
},
);

RadioGroup.displayName = 'RadioGroup';
};

export { RadioGroup, RadioItem, RadioIndicator };
export { RadioGroup };