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

Radios component #119

Merged
merged 10 commits into from
Jun 20, 2024
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
};