Skip to content

Commit

Permalink
feat: add Radio styles to Pentaho Plus theme
Browse files Browse the repository at this point in the history
  • Loading branch information
plagoa committed Mar 27, 2024
1 parent 9869fc6 commit 64c7d25
Show file tree
Hide file tree
Showing 8 changed files with 228 additions and 41 deletions.
51 changes: 48 additions & 3 deletions packages/core/src/BaseRadio/BaseRadio.styles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,28 @@ import { createClasses } from "../utils/classes";
export const { staticClasses, useClasses } = createClasses("HvBaseRadio", {
root: {
padding: 0,
width: 32,
minWidth: 32,
height: 32,
cursor: "pointer",
"&:hover": {
backgroundColor: theme.colors.containerBackgroundHover,
borderRadius: theme.radii.base,
},
"& svg": {
width: 16,
height: 16,
borderRadius: theme.radii.circle,
border: `1px solid ${theme.colors.secondary}`,
},
borderRadius: 0,
},
disabled: {
cursor: "not-allowed",
pointerEvents: "initial",
"& svg": {
"& path:nth-of-type(2)": {
fill: theme.colors.secondary_60,
},
border: `1px solid ${theme.colors.secondary_60}`,
backgroundColor: theme.colors.atmo3,
},
},
focusVisible: {
Expand All @@ -29,4 +37,41 @@ export const { staticClasses, useClasses } = createClasses("HvBaseRadio", {
},
},
icon: {},
checked: {
"& svg": {
border: `1px solid ${theme.colors.secondary}`,
backgroundColor: theme.colors.secondary,
color: theme.colors.atmo2,
},
"&$semantic": {
"& svg": {
border: `1px solid ${theme.colors.base_dark}`,
backgroundColor: theme.colors.base_light,
color: theme.colors.base_dark,
},
},
"&$disabled": {
"& svg": {
border: `1px solid ${theme.colors.secondary_60}`,
backgroundColor: theme.colors.secondary_60,
color: theme.colors.atmo3,
},
},
},
semantic: {
"& svg": {
border: `1px solid ${theme.colors.base_dark}`,
backgroundColor: theme.colors.base_light,
},
},
pagination: {
minWidth: 0,
width: 16,
height: 16,
"& svg": {
border: "none",
width: "unset",
height: "unset",
},
},
});
41 changes: 15 additions & 26 deletions packages/core/src/BaseRadio/BaseRadio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,11 @@ import { useState, useCallback, forwardRef } from "react";

import MuiRadio, { RadioProps as MuiRadioProps } from "@mui/material/Radio";

import {
RadioButtonUnselected,
RadioButtonSelected,
} from "@hitachivantara/uikit-react-icons";

import { useDefaultProps } from "../hooks/useDefaultProps";
import { ExtractNames } from "../utils/classes";

import { staticClasses, useClasses } from "./BaseRadio.styles";
import { Selected, Unselected } from "./icons";

export { staticClasses as baseRadioClasses };

Expand Down Expand Up @@ -74,6 +70,10 @@ export interface HvBaseRadioProps
* Whether the selector should use semantic colors.
*/
semantic?: boolean;
/**
* Whether the selector is used on a pagination layout.
*/
pagination?: boolean;
/**
* Properties passed on to the input element.
*/
Expand All @@ -89,25 +89,10 @@ export interface HvBaseRadioProps
onBlur?: (event: React.FocusEvent<any>) => void;
}

export const getSelectorIcons = (
options: { disabled: boolean; semantic: boolean },
classes: HvBaseRadioClasses
) => {
const { disabled, semantic } = options;
const color =
(disabled && ["atmo3", "secondary_60"]) ||
(semantic && ["base_light", "base_dark"]) ||
undefined;
const checkedColor =
(disabled && ["atmo3", "secondary_60"]) ||
(semantic && ["base_dark", "base_light"]) ||
undefined;

export const getSelectorIcons = () => {
return {
radio: <RadioButtonUnselected color={color} className={classes.icon} />,
radioChecked: (
<RadioButtonSelected color={checkedColor} className={classes.icon} />
),
radio: <Unselected />, // <RadioButtonUnselected color={color} className={classes.icon} />,
radioChecked: <Selected />,
};
};

Expand All @@ -132,6 +117,7 @@ export const HvBaseRadio = forwardRef<HTMLButtonElement, HvBaseRadioProps>(
defaultChecked,
onChange,
semantic = false,
pagination = false,
inputProps,
onFocusVisible,
onBlur,
Expand All @@ -158,7 +144,7 @@ export const HvBaseRadio = forwardRef<HTMLButtonElement, HvBaseRadioProps>(
[onBlur]
);

const icons = getSelectorIcons({ disabled, semantic }, classes);
const icons = getSelectorIcons();

const onLocalChange = useCallback(
(evt: React.ChangeEvent<HTMLInputElement>) => {
Expand All @@ -181,11 +167,14 @@ export const HvBaseRadio = forwardRef<HTMLButtonElement, HvBaseRadioProps>(
{
[classes.disabled]: disabled,
[classes.focusVisible]: focusVisible,
[classes.checked]: checked,
[classes.semantic]: semantic,
[classes.pagination]: pagination,
},
className
)}
icon={icons.radio}
checkedIcon={icons.radioChecked}
icon={others.icon || icons.radio}
checkedIcon={others.checkedIcon || icons.radioChecked}
color="default"
disabled={disabled}
required={required}
Expand Down
14 changes: 14 additions & 0 deletions packages/core/src/BaseRadio/icons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const Selected = () => {
return (
<svg width="14" height="14" xmlns="http://www.w3.org/2000/svg">
<path
d="m7,11a4,4 0 0 1 -4,-4a4,4 0 0 1 4,-4a4,4 0 0 1 4,4a4,4 0 0 1 -4,4"
fill="currentcolor"
/>
</svg>
);
};

export const Unselected = () => {
return <svg width="14" height="14" xmlns="http://www.w3.org/2000/svg" />;
};
1 change: 1 addition & 0 deletions packages/core/src/DotPagination/DotPagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ export const HvDotPagination = (props: HvDotPaginationProps) => {
icon={icons.radio}
checkedIcon={icons.radioChecked}
aria-label={getItemAriaLabel?.(i)}
pagination
/>
))}
</HvRadioGroup>
Expand Down
127 changes: 115 additions & 12 deletions packages/core/src/Radio/Radio.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,19 +61,85 @@ export const Variants: StoryObj<HvRadioProps> = {
),
],
render: () => {
const styles = {
root: css({
display: "flex",
flexDirection: "column",
gap: 20,
flexWrap: "wrap",
}),
group: css({
display: "flex",
flexDirection: "row",
gap: 20,
}),
};

return (
<>
<HvRadio required name="required" label="Required" value="1" />
<HvRadio disabled name="disabled" label="Disabled" value="1" />
<HvRadio readOnly name="readonly" label="Readonly" value="1" />
<HvRadio
status="invalid"
statusMessage="Oh no!"
name="invalid"
label="Invalid"
value="1"
/>
</>
<div className={styles.root}>
<HvTypography variant="title3">Disabled</HvTypography>
<div className={styles.group}>
<HvRadio disabled name="disabled" label="Disabled" value="1" />
<HvRadio
disabled
name="disabled"
defaultChecked
label="Disabled"
value="1"
/>
</div>
<HvTypography variant="title3">Readonly</HvTypography>
<div className={styles.group}>
<HvRadio readOnly name="readonly" label="Readonly" value="1" />
<HvRadio
readOnly
name="readonly"
defaultChecked
label="Readonly"
value="1"
/>
</div>
<HvTypography variant="title3">Required</HvTypography>
<div className={styles.group}>
<HvRadio required name="required" label="Required" value="1" />
<HvRadio
required
name="required"
defaultChecked
label="Required"
value="1"
/>
</div>
<HvTypography variant="title3">Invalid</HvTypography>
<div className={styles.group}>
<HvRadio
status="invalid"
statusMessage="Oh no!"
name="invalid"
label="Invalid"
value="1"
/>
<HvRadio
status="invalid"
statusMessage="Oh no!"
name="invalid"
defaultChecked
label="Invalid"
value="1"
/>
</div>
<HvTypography variant="title3">Semantic</HvTypography>
<div className={styles.group}>
<HvRadio semantic name="semantic" label="Semantic" value="1" />
<HvRadio
semantic
name="semantic"
defaultChecked
label="Semantic"
value="1"
/>
</div>
</div>
);
},
};
Expand Down Expand Up @@ -204,3 +270,40 @@ export const ExternalErrorMessage: StoryObj<HvRadioProps> = {
);
},
};

export const Custom: StoryObj<HvRadioProps> = {
parameters: {
eyes: { include: false },
},
render: () => {
const styles = {
group: css({
display: "flex",
flexDirection: "row",
gap: 20,
}),
box: css({
"& svg": {
borderRadius: "6px",
border: `1px solid ${theme.colors.warning}`,
},
}),
checked: css({
"& svg": {
border: `1px solid ${theme.colors.warning}`,
backgroundColor: theme.colors.atmo1,
color: theme.colors.warning,
},
}),
};

return (
<div className={styles.group}>
<HvRadio
label="Radio 1"
classes={{ root: styles.box, checked: styles.checked }}
/>
</div>
);
},
};
2 changes: 2 additions & 0 deletions packages/core/src/Radio/Radio.styles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,6 @@ export const { staticClasses, useClasses } = createClasses("HvRadio", {
width: "100%",
},
focusVisible: { backgroundColor: theme.colors.atmo3, ...outlineStyles },
checked: {},
semantic: {},
});
8 changes: 8 additions & 0 deletions packages/core/src/Radio/Radio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ export interface HvRadioProps
* Whether the selector should use semantic colors.
*/
semantic?: boolean;
/**
* Whether the selector is used on a pagination layout.
*/
pagination?: boolean;
/**
* Properties passed on to the input element.
*/
Expand Down Expand Up @@ -154,6 +158,7 @@ export const HvRadio = forwardRef<HTMLButtonElement, HvRadioProps>(
statusMessage,
"aria-errormessage": ariaErrorMessage,
semantic = false,
pagination = false,
inputProps,
onFocusVisible,
onBlur,
Expand Down Expand Up @@ -229,6 +234,7 @@ export const HvRadio = forwardRef<HTMLButtonElement, HvRadioProps>(
value={value}
checked={isChecked}
semantic={semantic}
pagination={pagination}
inputProps={{
"aria-invalid": isStateInvalid ? true : undefined,
"aria-errormessage": errorMessageId,
Expand Down Expand Up @@ -259,6 +265,8 @@ export const HvRadio = forwardRef<HTMLButtonElement, HvRadioProps>(
[classes.disabled]: disabled,
[classes.focusVisible]: !!(focusVisible && label),
[classes.invalidContainer]: isStateInvalid,
[classes.checked]: isChecked,
[classes.semantic]: semantic,
})}
>
{radio}
Expand Down
Loading

0 comments on commit 64c7d25

Please sign in to comment.