Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Radio component #370

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
13 changes: 13 additions & 0 deletions components/Radio/ReadMe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script lang="ts" setup>
import Radio from '@cypress-design/vue-radio'
</script>

# Radio

<DemoWrapper>
<Radio label="Some Labels" name="example"/>
</DemoWrapper>

Describe your component here.

[figma::Radio](https://www.figma.com/file/1WJ3GVQyMV5e7xVxPg3yID/Design-System?node-id=1927%3A2466&mode=dev)
7 changes: 7 additions & 0 deletions components/Radio/assertions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/// <reference types="cypress" />

export default function assertions(mountStory: (options?: any) => void): void {
it('renders', () => {
mountStory()
})
}
20 changes: 20 additions & 0 deletions components/Radio/constants/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "@cypress-design/constants-radio",
"private": true,
"version": "0.0.1",
"files": [
"*"
],
"main": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"scripts": {
"build": "tsc --project ./tsconfig.json"
},
"license": "MIT"
}
43 changes: 43 additions & 0 deletions components/Radio/constants/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
export const Classes = {
wrapper: 'relative flex items-center',
hiddenInput: 'absolute inset-0 w-0 h-0 opacity-0',
labelTag: 'flex items-center',
visibleRadio:
'border border-solid rounded-full h-[16px] w-[16px] flex flex-shrink-0 items-center text-white outline',
visibleRadioIndicator:
'rounded-full h-[6px] w-[6px] items-center absolute ml-auto mr-auto inset-x-0',
trueLabel: 'block ml-[8px] text-[16px] leading-[24px] font-light select-none',
}

export const RadioColors = {
indigo: 'border-indigo-500 bg-indigo-400',
jade: 'border-jade-500 bg-jade-400',
red: 'border-red-500 bg-red-400',
disabled: 'border-gray-200 bg-gray-100',
empty: 'border-gray-200 bg-white',
} as const

export interface RadioProps {
/**
* A unique identifier for the checkbox on the whole page.
* It will be used to give match label with input for a11y.
*/
id?: string
/**
* Name attribute of the <input type="radio"/>.
*/
name?: string
/**
* Is the radio checked when it is first rendered.
*/
checked?: boolean
/**
* The color of the background in the checkbox.
* The check mark will always be white.
*/
color?: keyof Omit<typeof RadioColors, 'disabled' | 'empty'>
/**
* If the radio is disabled, it will not be clickable.
*/
disabled?: boolean
}
12 changes: 12 additions & 0 deletions components/Radio/constants/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"extends": "../../../tsconfig.json",
"include": ["src/*.ts"],
"compilerOptions": {
"rootDir": "./src",
"noEmit": false,
"declaration": true,
"declarationMap": true,
"outDir": "dist",
"types": []
}
}
1 change: 1 addition & 0 deletions components/Radio/react/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# @cypress-design/react-radio
40 changes: 40 additions & 0 deletions components/Radio/react/Radio.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import * as React from 'react'
import type { HTMLProps, ReactNode } from 'react'
import clsx from 'clsx'
import { type RadioProps as GenericRadioProps } from '@cypress-design/constants-radio'

export interface RadioProps
extends Omit<
HTMLProps<HTMLDivElement>,
'label' | 'onChange' | 'name' | 'color'
>,
GenericRadioProps {
/**
* Label for the radio.
* It is very important to set this to make the checkbox accessible.
*/
label?: ReactNode
/**
* Body content for the radio (for example, description text for the Radio field)
*/
body?: ReactNode
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void
}

export const RadioInput: React.FC<
RadioProps & React.HTMLProps<HTMLDivElement>
> = ({ id, label, body, className, name, ...rest }) => {
return (
<div className={clsx(className)} {...rest}>
<span>
<input type="radio" id={id} name={name} />
<div>
<label htmlFor={id}>{label}</label>
{body}
</div>
</span>
</div>
)
}

export default RadioInput
13 changes: 13 additions & 0 deletions components/Radio/react/RadioReact.cy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/// <reference types="cypress" />

import * as React from 'react'
import { mount } from 'cypress/react18'
import Radio from './Radio'
import assertions from '../assertions'

describe('Radio', () => {
function mountStory(options: Parameters<typeof Radio>[0] = { id: '1' }) {

Check failure on line 9 in components/Radio/react/RadioReact.cy.tsx

View workflow job for this annotation

GitHub Actions / Test / cypress-run

Type '{ id: string; }' is not assignable to type 'RadioProps & HTMLProps<HTMLDivElement>'.
mount(<Radio {...options} />)
}
assertions(mountStory)
})
13 changes: 13 additions & 0 deletions components/Radio/react/ReadMe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Radio

## Install

```bash
npm install @cypress-design/react-radio
```

or with yarn

```bash
yarn add @cypress-design/react-radio
```
1 change: 1 addition & 0 deletions components/Radio/react/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './Radio'
30 changes: 30 additions & 0 deletions components/Radio/react/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "@cypress-design/react-radio",
"version": "0.0.1",
"files": [
"*"
],
"typings": "./dist/index.d.ts",
"module": "./dist/index.es.mjs",
"main": "./dist/index.umd.js",
"exports": {
".": {
"import": "./dist/index.es.mjs",
"require": "./dist/index.umd.js"
}
},
"scripts": {
"build": "yarn build:module && yarn build:types",
"build:module": "rollup -c ./rollup.config.mjs",
"build:types": "tsc --project ./tsconfig.build.json"
},
"dependencies": {
"@cypress-design/constants-radio": "*",
"@cypress-design/css": "^0.16.0",
"clsx": "*"
},
"devDependencies": {
"@cypress-design/rollup-plugin-tailwind-keep": "*"
},
"license": "MIT"
}
3 changes: 3 additions & 0 deletions components/Radio/react/rollup.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import rootRollupConfig from '../../root.rollup.config.mjs'

export default rootRollupConfig({ input: './index.ts' })
8 changes: 8 additions & 0 deletions components/Radio/react/tsconfig.build.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "../../../tsconfig.react.build.json",
"include": ["./Radio.tsx", "./index.ts"],
"compilerOptions": {
"sourceRoot": "./",
"outDir": "dist"
}
}
1 change: 1 addition & 0 deletions components/Radio/vue/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# @cypress-design/vue-radio
91 changes: 91 additions & 0 deletions components/Radio/vue/Radio.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<script lang="ts">
function uid() {
return String(Date.now().toString(32) + Math.random().toString(16)).replace(
/\./g,
'',
)
}
</script>

<script lang="ts" setup>
import { ref, computed } from 'vue'
import {
RadioColors,
Classes,
RadioProps,
} from '@cypress-design/constants-radio'

const props = withDefaults(
defineProps<
RadioProps & {
/**
* Label for the checkbox.
* It is very important to set this to make the checkbox accessible.
*/
label?: string
}
>(),
{
id: () => uid(),
color: 'indigo',
},
)

const localChecked = ref(props.checked)

const emit = defineEmits<{
/**
* Fired when the radio changes value
* @arg value - The next value of the radio
*/
(event: 'update:modelValue', value: boolean | Array<string>): void
(event: 'change', value: boolean): void
}>()

function updated() {
localChecked.value = !localChecked.value
emit('change', localChecked.value)
}

const checkboxClasses = computed(() =>
props.disabled
? RadioColors.disabled
: localChecked.value
? RadioColors[props.color]
: RadioColors.empty,
)
</script>

<template>
<span class="relative flex items-center">
<input
:id="id"
:class="Classes.hiddenInput"
:name="name || id"
type="radio"
@click="updated"
:disabled="props.disabled"
:checked="localChecked"
/>
<label :class="Classes.labelTag" :for="id">
<div :class="[Classes.wrapper]">
<span :class="[Classes.visibleRadio]" />
<span
v-if="localChecked"
:class="[checkboxClasses, Classes.visibleRadioIndicator]"
/>
</div>
<slot name="label">
<span
v-if="label"
:class="[
Classes.trueLabel,
disabled ? 'text-gray-500' : 'text-gray-800',
]"
>
{{ label }}
</span>
</slot>
</label>
</span>
</template>
33 changes: 33 additions & 0 deletions components/Radio/vue/RadioVue.cy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/// <reference types="cypress" />
import { mount } from 'cypress/vue'
import Radio from './Radio.vue'

describe('<Radio/>', () => {
it('renders with label text as a string', () => {
mount(() => <Radio label="Hello" id="some-radio" />)
})

it('renders with label text as a component', () => {
mount(() => <Radio label="Hello" id="some-radio" />)
})

it('renders without label text', () => {
mount(() => <Radio label="Hello" id="some-radio" />)
})

it('changes when the label is clicked', () => {
mount(() => <Radio label="Hello" id="some-radio" />)
})

it('changes when the radio is clicked', () => {
mount(() => <Radio label="Hello" id="some-radio" />)
})

it('cannot be clicked when disabled', () => {
mount(() => <Radio label="Hello" id="some-radio" disabled />)
})

it('can be marked as checked, even if disabled', () => {
mount(() => <Radio label="Hello" id="some-radio" disabled checked />)
})
})
13 changes: 13 additions & 0 deletions components/Radio/vue/ReadMe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Radio

## Install

```bash
npm install @cypress-design/vue-radio
```

or with yarn

```bash
yarn add @cypress-design/vue-radio
```
1 change: 1 addition & 0 deletions components/Radio/vue/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './Radio.vue'
30 changes: 30 additions & 0 deletions components/Radio/vue/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "@cypress-design/vue-radio",
"version": "0.0.1",
"files": [
"*"
],
"typings": "./dist/index.d.ts",
"module": "./dist/index.es.mjs",
"main": "./dist/index.umd.js",
"exports": {
".": {
"import": "./dist/index.es.mjs",
"require": "./dist/index.umd.js",
"types": "./dist/index.d.ts"
}
},
"scripts": {
"build": "yarn build:module && yarn build:types",
"build:module": "yarn vite build",
"build:types": "yarn vue-tsc --project ./tsconfig.build.json"
},
"dependencies": {
"@cypress-design/constants-radio": "*",
"@cypress-design/css": "^0.16.0"
},
"devDependencies": {
"@cypress-design/rollup-plugin-tailwind-keep": "*"
},
"license": "MIT"
}