Skip to content

Commit

Permalink
Add error styling updates (#58)
Browse files Browse the repository at this point in the history
* Add error styling updates

* Update story and create custom checkbox styling

* Update version and add changelog

* update snapshot

* Add svg icon file and import to checkbox
  • Loading branch information
MReyna12 authored Oct 24, 2024
1 parent 54244e9 commit 5e0eb08
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 26 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Changelog

All notable changes to this project will be documented in this file.

## [v1.10.7] - 2024-10-22

- Create custom styled checkbox
- Update primary and light checkbox styles
- Update color for checkbox label
- Add disabled state style
- Add error state style
- Add error prop to checkbox
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@openstax/ui-components",
"version": "1.10.6",
"version": "1.10.7",
"license": "MIT",
"source": "./src/index.ts",
"types": "./dist/index.d.ts",
Expand Down
79 changes: 63 additions & 16 deletions src/components/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,84 @@ import { LabelHTMLAttributes, PropsWithChildren } from "react";
import { colors } from "../theme";
import styled from "styled-components";
import { InputHTMLAttributes } from "react";
import { whiteCheckmark, grayCheckmark, redCheckmark } from "./svgs/checkmarksvgs";

type CheckboxVariant = keyof typeof checkboxVariants;
type CheckboxSize = 1.4 | 1.6 | 1.8 | 2;
export type CheckboxVariant = keyof typeof checkboxVariants;
export type CheckboxSize = 1.4 | 1.6 | 1.8 | 2;

export const checkboxVariants = {
primary: {
accentColor: colors.palette.mediumBlue,
boxShadow: 'none',
backgroundColor: colors.palette.mediumBlue,
color: 'inherit',
unCheckedBorder: `1px solid ${colors.palette.neutralThin}`,
checkedBorder: `1px solid ${colors.palette.mediumBlue}`,
backgroundImage: whiteCheckmark
},
light: {
accentColor: colors.palette.white,
boxShadow: '0 0 1px 0',
backgroundColor: colors.palette.white,
color: 'inherit',
unCheckedBorder: `1px solid ${colors.palette.pale}`,
checkedBorder: `1px solid ${colors.palette.pale}`,
backgroundImage: grayCheckmark
},
error: {
backgroundColor: colors.palette.paleRed,
color: colors.palette.darkRed,
unCheckedBorder: `1px solid ${colors.palette.lightRed}`,
checkedBorder: `1px solid ${colors.palette.lightRed}`,
backgroundImage: redCheckmark
},
disabled: {
backgroundColor: colors.palette.white,
color: 'inherit',
unCheckedBorder: `1px solid ${colors.palette.pale}`,
checkedBorder: `1px solid ${colors.palette.pale}`,
backgroundImage: 'none'
}
} as const;

const StyledLabel = styled.label<{ bold: boolean; }>`
const StyledLabel = styled.label<{ bold: boolean; variant: CheckboxVariant; isDisabled?: boolean; }>`
font-size: 1.4rem;
display: flex;
align-items: center;
font-weight: ${props => props.bold ? 700 : 400}
font-weight: ${props => props.bold ? 700 : 400};
color: ${(props => props.isDisabled ? colors.palette.neutralLight : checkboxVariants[props.variant].color)};
`;

const StyledInput = styled.input<{ variant: CheckboxVariant; checkboxSize: CheckboxSize; }>`
accent-color: ${props => checkboxVariants[props.variant].accentColor};
// https://moderncss.dev/pure-css-custom-checkbox-style/
const StyledInput = styled.input<{ variant: CheckboxVariant; checkboxSize: CheckboxSize; isDisabled?: boolean; }>`
appearance: none;
/* For iOS < 15 to remove gradient background */
background-color: ${colors.palette.white};
opacity: ${(props => props.isDisabled ? '0.4' : '1')};
border: ${props => props.isDisabled ? `1px solid ${colors.palette.pale}` : checkboxVariants[props.variant].unCheckedBorder};
border-radius: 0.2rem;
transform: translateY(-0.075em);
width: ${props => props.checkboxSize}rem;
height: ${props => props.checkboxSize}rem;
margin: 0 1.6rem 0 0;
&:checked {
box-shadow: ${props => checkboxVariants[props.variant].boxShadow};
display: grid;
place-content: center;
&::before {
content: "";
width: ${props => props.checkboxSize}rem;
height: ${props => props.checkboxSize}rem;
border: ${props => checkboxVariants[props.variant].checkedBorder};
border-radius: 0.2rem;
transform: scale(0);
background-color: ${props => checkboxVariants[props.variant].backgroundColor};
background-image: url('${props => checkboxVariants[props.variant].backgroundImage}');
background-size: 80%;
background-position: center;
background-repeat: no-repeat;
}
`;
&:checked::before {
transform: scale(1);
opacity: ${(props => props.isDisabled ? 0 : 1)};
}
`;

type CheckboxProps = PropsWithChildren<
Omit<InputHTMLAttributes<HTMLInputElement>, 'type'> & {
Expand All @@ -42,10 +89,10 @@ type CheckboxProps = PropsWithChildren<
labelProps?: LabelHTMLAttributes<HTMLLabelElement>;
}>;

export const Checkbox = ({ children, variant = 'primary', bold = false, size = 1.6, labelProps, ...props }: CheckboxProps) => {
export const Checkbox = ({ children, disabled, variant = 'primary', bold = false, size = 1.6, labelProps, ...props }: CheckboxProps) => {
return (
<StyledLabel bold={bold} {...labelProps}>
<StyledInput {...props} type="checkbox" variant={variant} checkboxSize={size} />
<StyledLabel bold={bold} variant={variant} isDisabled={disabled} {...labelProps}>
<StyledInput {...props} type="checkbox" variant={variant} checkboxSize={size} isDisabled={disabled} />
{children}
</StyledLabel>
);
Expand Down
12 changes: 6 additions & 6 deletions src/components/__snapshots__/Checkbox.spec.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
exports[`Checkbox allows setting props on label 1`] = `
<label
aria-label="custom label"
className="sc-bczRLJ bYTfrb"
className="sc-bczRLJ brUlbM"
>
<input
className="sc-gsnTZi hvmWuC"
className="sc-gsnTZi gDsstp"
type="checkbox"
/>
Click Me
Expand All @@ -15,10 +15,10 @@ exports[`Checkbox allows setting props on label 1`] = `

exports[`Checkbox handles options 1`] = `
<label
className="sc-bczRLJ bYTfrb"
className="sc-bczRLJ brUlbM"
>
<input
className="sc-gsnTZi hvmWuC"
className="sc-gsnTZi gDsstp"
type="checkbox"
/>
Click Me
Expand All @@ -27,10 +27,10 @@ exports[`Checkbox handles options 1`] = `

exports[`Checkbox matches snapshot 1`] = `
<label
className="sc-bczRLJ bYTeUU"
className="sc-bczRLJ gtxOzv"
>
<input
className="sc-gsnTZi ljReaQ"
className="sc-gsnTZi bBmYov"
type="checkbox"
/>
Click Me
Expand Down
42 changes: 42 additions & 0 deletions src/components/forms/uncontrolled/inputType.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import styled from "styled-components";
import { Checkbox } from "./inputTypes";


const CheckboxGroup = styled.div`
& + & {
margin-top: 3.2rem;
}
> * + * {
margin-top: 0.5rem;
}
`;

type CheckboxProps = React.ComponentProps<typeof Checkbox>;
const renderCheckboxes = (props: CheckboxProps) =>
<CheckboxGroup>
<h2>Size {props.size}</h2>
<Checkbox {...props} defaultChecked></Checkbox>
<Checkbox {...props} defaultChecked></Checkbox>
<Checkbox {...props} defaultChecked></Checkbox>
</CheckboxGroup>;

export const defaultCheckbox = () => <>
{renderCheckboxes({error: [], label: 'Checkbox Label', variant: 'primary', size: 1.4})}
{renderCheckboxes({error: [], label: 'Checkbox Label', variant: 'primary', size: 1.6})}
{renderCheckboxes({error: undefined, label: 'Checkbox Label', variant: 'primary', size: 1.8})}
{renderCheckboxes({error: undefined, label: 'Checkbox Label', variant: 'primary', size: 2.0})}
</>;

export const errorCheckbox = () => <>
{renderCheckboxes({error: ['Error messages go here'], label: 'Checkbox Label', variant: 'error', size: 1.4})}
{renderCheckboxes({error: ['Error messages go here'], label: 'Checkbox Label', variant: 'error', size: 1.6})}
{renderCheckboxes({error: ['Error messages go here'], label: 'Checkbox Label', variant: 'error', size: 1.8})}
{renderCheckboxes({error: ['Error messages go here'], label: 'Checkbox Label', variant: 'error', size: 2.0})}
</>;

export const disabledCheckbox = () => <>
{renderCheckboxes({error: [], disabled: true, label: 'Checkbox Label', variant: 'disabled', size: 1.4})}
{renderCheckboxes({error: [], disabled: true, label: 'Checkbox Label', variant: 'disabled', size: 1.6})}
{renderCheckboxes({error: [], disabled: true, label: 'Checkbox Label', variant: 'disabled', size: 1.8})}
{renderCheckboxes({error: [], disabled: true, label: 'Checkbox Label', variant: 'disabled', size: 2.0})}
</>;
23 changes: 20 additions & 3 deletions src/components/forms/uncontrolled/inputTypes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { FormInputWrapper, FormLabelText, HelpText, InputProps, RequiredIndicato
import { AbstractFormData } from "../controlled/hooks";
import { partitionSequence } from "@openstax/ts-utils/misc/partitionSequence";
import { Radio as StyledRadio } from "../../Radio";
import { Checkbox as StyledCheckbox, CheckboxSize, CheckboxVariant} from "../../Checkbox"

/*
* input element
Expand Down Expand Up @@ -195,28 +196,44 @@ export const Radio = ({
type CheckboxProps = React.ComponentPropsWithoutRef<'input'> & InputProps & {
onChangeValue?: (value: boolean | undefined) => void;
wrapperProps?: React.ComponentPropsWithoutRef<'label'>;
error?: string[];
size?: CheckboxSize;
variant?: CheckboxVariant;
};
const CheckboxLine = styled.div`
flex-direction: row;
display: flex;
align-items: center;
`;
const StyledErrorMessage = styled.p`
color: #C22032;
font-size: 1.4rem;
margin: 0;
padding: 0;
line-height: 2.5rem;
`
export const Checkbox = ({
label,
help,
wrapperProps,
error,
onChangeValue,
...props
}: CheckboxProps) => {
return <FormInputWrapper {...wrapperProps}>
<CheckboxLine>
<input type="checkbox" {...props} onChange={e => {
<StyledCheckbox {...props} onChange={e => {
onChangeValue?.(!!e.target.checked);
props.onChange?.(e);
}}/>
<FormLabelText><RequiredIndicator show={props.required} />{label}</FormLabelText>
}}
>
<FormLabelText><RequiredIndicator show={props.required} />{label}</FormLabelText>
</StyledCheckbox>
</CheckboxLine>
<HelpText value={help} />
{error !== undefined && error.length > 0 &&
<StyledErrorMessage>This activity has been deleted. Please deselect this activity to remove it from this assignment and avoid errors.</StyledErrorMessage>
}
</FormInputWrapper>;
};

Expand Down
5 changes: 5 additions & 0 deletions src/components/svgs/checkmarksvgs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const whiteCheckmark = 'data:image/svg+xml,<svg height="125px" width="125px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 17.837 17.837" xml:space="preserve" fill="%23000000"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><g><path style="fill:%23fff;" d="M16.145,2.571c-0.272-0.273-0.718-0.273-0.99,0L6.92,10.804l-4.241-4.27 c-0.272-0.274-0.715-0.274-0.989,0L0.204,8.019c-0.272,0.271-0.272,0.717,0,0.99l6.217,6.258c0.272,0.271,0.715,0.271,0.99,0 L17.63,5.047c0.276-0.273,0.276-0.72,0-0.994L16.145,2.571z"></path></g></g></svg>'

export const grayCheckmark = 'data:image/svg+xml,<svg height="125px" width="125px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 17.837 17.837" xml:space="preserve" fill="%23000000"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><g><path style="fill:%235e5e5e;" d="M16.145,2.571c-0.272-0.273-0.718-0.273-0.99,0L6.92,10.804l-4.241-4.27 c-0.272-0.274-0.715-0.274-0.989,0L0.204,8.019c-0.272,0.271-0.272,0.717,0,0.99l6.217,6.258c0.272,0.271,0.715,0.271,0.99,0 L17.63,5.047c0.276-0.273,0.276-0.72,0-0.994L16.145,2.571z"></path></g></g></svg>'

export const redCheckmark = 'data:image/svg+xml,<svg height="125px" width="125px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 17.837 17.837" xml:space="preserve" fill="%23000000"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><g><path style="fill:%23C22032;" d="M16.145,2.571c-0.272-0.273-0.718-0.273-0.99,0L6.92,10.804l-4.241-4.27 c-0.272-0.274-0.715-0.274-0.989,0L0.204,8.019c-0.272,0.271-0.272,0.717,0,0.99l6.217,6.258c0.272,0.271,0.715,0.271,0.99,0 L17.63,5.047c0.276-0.273,0.276-0.72,0-0.994L16.145,2.571z"></path></g></g></svg>'

0 comments on commit 5e0eb08

Please sign in to comment.