Skip to content

Commit

Permalink
Merge pull request #119 from communitiesuk/radio-component
Browse files Browse the repository at this point in the history
Radios component
  • Loading branch information
kateriley1 committed Jun 20, 2024
2 parents e64bf36 + 89bb05a commit 0094ede
Show file tree
Hide file tree
Showing 9 changed files with 224 additions and 10 deletions.
3 changes: 3 additions & 0 deletions example.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@
defaultTab=0,
tabHeadings=["Display jitter plots", "Display time series plots"],
),
uk_gov_dash_components.Radios(
options=["a", "b"], value="b", id="test", title="Which do you prefer?"
),
]
)

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "uk_gov_dash_components",
"version": "1.25.0",
"version": "1.26.0",
"description": "Dash components for Gov UK",
"repository": {
"type": "git",
Expand Down
8 changes: 5 additions & 3 deletions src/demo/App.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
/* eslint no-magic-numbers: 0 */
import React, { useState } from 'react';

import { Accordion, CheckboxList, ComponentTemplate, Dropdown, ExpandableMenuItem } from '../lib';

import Tabs from '../lib/fragments/Tabs.react';
import { Accordion, CheckboxList, ComponentTemplate, Dropdown, ExpandableMenuItem, Radios, Tabs } from '../lib';

import './dashboard.css';

Expand Down Expand Up @@ -91,6 +89,10 @@ const App = () => {
</a>
</li>
</ul>
<h2>Radios</h2>
<div>
<Radios id="radios" value={["restrict"]} options={["restrict", "restrict1"]} setProps={setProps} />
</div>
<Accordion id="accordion" accordionHeadings={["charts", 'empty', "data!!!!!"]} defaultSectionsOpen={[false, false, true]} children={[<p>I am a child<br /></p>, <p>I am a empty</p>, <p>I am a hat</p>]} bannerSections={[2, null, 0]}></Accordion>
<Tabs id="tabs" tabHeadings={["Display jitter plots", 'data', "Display time series pl}ots"]} defaultTab={0} children={[<div><p>I am a jitter plot</p></div>, <div><p>I am a jitter plot</p></div>, <div><p>I am a time series plot</p></div>]}></Tabs>
</>
Expand Down
3 changes: 2 additions & 1 deletion src/lib/LazyLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import ComponentTemplate from './fragments/ComponentTemplate.react'
import Dropdown from './fragments/Dropdown.react'
import ExpandableMenuItem from './fragments/ExpandableMenuItem.react'
import Tabs from './fragments/Tabs.react'
import Radios from './fragments/Radios.react'

export {
Accordion, AdditionalDetails, AutoComplete, ChangeLogBanner, CheckboxList, ComboBox, ComponentTemplate, Dropdown, ExpandableMenuItem, Tabs
Accordion, AdditionalDetails, AutoComplete, ChangeLogBanner, CheckboxList, ComboBox, ComponentTemplate, Dropdown, ExpandableMenuItem, Tabs, Radios
}

121 changes: 121 additions & 0 deletions src/lib/components/Radios.react.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Radios as RealComponent } from '../LazyLoader';

/**
* Lazy loaded Radios
*
* @param {
* id,
* title,
* options,
* value,
* } [props={}]
* @return {*}
*/
const Radios = (props = {}) => {
return (
<RealComponent {...props} />
);
}

/**
* PropTypes is a part of React, see full documenation below.
* https://reactjs.org/docs/typechecking-with-proptypes.html
*/

Radios.defaultProps = {
options: [],
value: [],
};

Radios.propTypes = {
/**
* An array of options
*/
options: PropTypes.oneOfType([
/**
* Array of options where the label and the value are the same thing - [string|number|bool]
*/
PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
])
),
/**
* Simpler `options` representation in dictionary format. The order is not guaranteed.
* {`value1`: `label1`, `value2`: `label2`, ... }
* which is equal to
* [{label: `label1`, value: `value1`}, {label: `label2`, value: `value2`}, ...]
*/
PropTypes.object,
/**
* An array of options {label: [string|number], value: [string|number]},
* an optional disabled field can be used for each option
*/
PropTypes.arrayOf(
PropTypes.exact({
/**
* The option's label
*/
label: PropTypes.node.isRequired,

/**
* The value of the option. This value
* corresponds to the items specified in the
* `value` property.
*/
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
]).isRequired,

/**
* If true, this option is disabled and cannot be selected.
*/
disabled: PropTypes.bool,

/**
* The HTML 'title' attribute for the option. Allows for
* information on hover. For more information on this attribute,
* see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/title
*/
title: PropTypes.string,
})
),
]),

/**
* The currently selected value
*/
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
]),

/**
* The ID of this component, used to identify dash components
* in callbacks. The ID needs to be unique across all of the
* components in an app.
*/
id: PropTypes.string,

/**
* The title to be displayed above all Radio items.
*/
title: PropTypes.string,

/**
* Dash-assigned callback that gets fired when the value changes.
*/
setProps: PropTypes.func,
};

export const defaultProps = Radios.defaultProps;
export const propTypes = Radios.propTypes;

export default Radios
3 changes: 1 addition & 2 deletions src/lib/fragments/CheckboxList.react.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {append, includes, without} from 'ramda';
import {append, includes, without, type} from 'ramda';
import React from 'react';
import {type} from 'ramda';
import { defaultProps, propTypes } from '../components/CheckboxList.react';

const sanitizeOptions = options => {
Expand Down
86 changes: 86 additions & 0 deletions src/lib/fragments/Radios.react.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { type } from 'ramda';

import React, { useState } from 'react';

import { defaultProps, propTypes } from '../components/Radios.react';

const sanitizeOptions = options => {
if (type(options) === 'Object') {
return Object.entries(options).map(([value, label]) => ({
label: React.isValidElement(label) ? label : String(label),
value,
}));
}

if (type(options) === 'Array') {
if (
options.length > 0 &&
['String', 'Number', 'Bool'].includes(type(options[0]))
) {
return options.map(option => ({
label: String(option),
value: option,
}));
}
return options;
}

return options;
};

/**
* Radios is a component that encapsulates several radios.
* The values and labels of the Radios list are specified in the `options`
* property and the selected item is specified with the `value` property.
* Only one Radio item can be selected at once.
* Each Radio item is rendered as an input with a surrounding label.
*/
const Radios = (props)=>{
const {
id,
title,
options,
setProps,
value: propValue,
} = { ...defaultProps, ...props };

// State to manage the selected radio value
const [value, setValue] = useState(propValue);

if (!id) { throw new Error('id is not defined') }

const handleChange = (newValue) => {
setValue(newValue); // Update local state
setProps({ value: newValue }); // Propagate value to parent component
};

return (
<div className='govuk-form-group' id={id}>
<fieldset className='govuk-fieldset'>
<legend className='govuk-fieldset__legend govuk-fieldset__legend--l'>
<label className="govuk-label">{title}</label>
</legend>
<div className="govuk-radios govuk-radios--small" data-module="govuk-radios">
{sanitizeOptions(options).map((option, index) => {
return (
<div className="govuk-radios__item" key={option.value}>
<input checked={value==option.value} className="govuk-radios__input"
type="radio" id={`${id}_option_${index}`} value={option.value} name={id}
onChange={() => handleChange(option.value)}
/>
<label className="govuk-label govuk-radios__label" htmlFor={`${id}_option_${index}`}>
{option.label}
</label>
</div>
);
})}
</div>
</fieldset>
</div>
)
}

Radios.defaultProps = defaultProps;
Radios.propTypes = propTypes;

export default Radios
4 changes: 3 additions & 1 deletion src/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Accordion from './components/Accordion.react';
import Tabs from './components/Tabs.react';
import ChangeLogBanner from './components/ChangeLogBanner.react';
import AdditionalDetails from './components/AdditionalDetails.react';
import Radios from './components/Radios.react';

export {
AutoComplete,
Expand All @@ -20,5 +21,6 @@ export {
Accordion,
Tabs,
ChangeLogBanner,
AdditionalDetails
AdditionalDetails,
Radios
};

0 comments on commit 0094ede

Please sign in to comment.