-
Notifications
You must be signed in to change notification settings - Fork 65
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
feat: add RadioGroup middleware #918
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import RadioGroup from '@dojo/widgets/radio-group'; | ||
import { create, tsx } from '@dojo/framework/core/vdom'; | ||
import { icache } from '@dojo/framework/core/middleware/icache'; | ||
|
||
const factory = create({ icache }); | ||
|
||
const App = factory(function({ properties, middleware: { icache } }) { | ||
const { get, set } = icache; | ||
|
||
return ( | ||
<virtual> | ||
<RadioGroup | ||
label="pets" | ||
name="standard" | ||
options={[{ value: 'cat' }, { value: 'dog' }, { value: 'fish' }]} | ||
onValue={(value) => { | ||
set('standard', value); | ||
}} | ||
/> | ||
<pre>{`${get('standard')}`}</pre> | ||
</virtual> | ||
); | ||
}); | ||
|
||
export default App; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import RadioGroup from '@dojo/widgets/radio-group'; | ||
import { create, tsx } from '@dojo/framework/core/vdom'; | ||
import { icache } from '@dojo/framework/core/middleware/icache'; | ||
|
||
const factory = create({ icache }); | ||
|
||
const App = factory(function({ properties, middleware: { icache } }) { | ||
const { get, set } = icache; | ||
|
||
return ( | ||
<virtual> | ||
<RadioGroup | ||
label="colours" | ||
name="colours" | ||
options={[ | ||
{ value: 'red', label: 'Rouge' }, | ||
{ value: 'green', label: 'Vert' }, | ||
{ value: 'blue', label: 'Bleu' } | ||
]} | ||
onValue={(value) => { | ||
set('colours', value); | ||
}} | ||
/> | ||
<pre>{`${get('colours')}`}</pre> | ||
</virtual> | ||
); | ||
}); | ||
|
||
export default App; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import RadioGroup from '@dojo/widgets/radio-group'; | ||
import { Radio } from '@dojo/widgets/radio'; | ||
import { create, tsx } from '@dojo/framework/core/vdom'; | ||
import { icache } from '@dojo/framework/core/middleware/icache'; | ||
|
||
const factory = create({ icache }); | ||
|
||
const App = factory(function({ properties, middleware: { icache } }) { | ||
const { get, set } = icache; | ||
|
||
return ( | ||
<virtual> | ||
<RadioGroup | ||
label="going?" | ||
name="custom" | ||
options={[{ value: 'yes' }, { value: 'no' }, { value: 'maybe' }]} | ||
onValue={(value) => { | ||
set('custom', value); | ||
}} | ||
renderer={(name, radioGroup, options) => { | ||
return options.map(({ value, label }) => { | ||
const { checked } = radioGroup(value); | ||
return ( | ||
<virtual> | ||
<span>I'm custom!</span> | ||
<Radio | ||
checked={checked()} | ||
label={label || value} | ||
name={name} | ||
onValue={checked} | ||
value={value} | ||
/> | ||
<hr /> | ||
</virtual> | ||
); | ||
}); | ||
}} | ||
/> | ||
<pre>{`${get('custom')}`}</pre> | ||
</virtual> | ||
); | ||
}); | ||
|
||
export default App; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import RadioGroup from '@dojo/widgets/radio-group'; | ||
import { create, tsx } from '@dojo/framework/core/vdom'; | ||
import { icache } from '@dojo/framework/core/middleware/icache'; | ||
|
||
const factory = create({ icache }); | ||
|
||
const App = factory(function({ properties, middleware: { icache } }) { | ||
const { get, set } = icache; | ||
|
||
return ( | ||
<virtual> | ||
<RadioGroup | ||
initialValue="tom" | ||
label="favourite names" | ||
name="initial-value" | ||
options={[{ value: 'tom' }, { value: 'dick' }, { value: 'harry' }]} | ||
onValue={(value) => { | ||
set('initial-value', value); | ||
}} | ||
/> | ||
<pre>{`${get('initial-value')}`}</pre> | ||
</virtual> | ||
); | ||
}); | ||
|
||
export default App; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# @dojo/widgets/radio-group widget | ||
|
||
Dojo's `RadioGroup` widget provides an opinionated way to use a group of check boxes in a form. | ||
|
||
## Features | ||
|
||
- Takes an options property to define the radios to create | ||
- Offers a custom renderer allowing the user to create their own radios | ||
- Provides a middleware for custom use |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import * as css from '../theme/radio-group.m.css'; | ||
import theme from '@dojo/framework/core/middleware/theme'; | ||
import { Radio } from '../radio/index'; | ||
import { RenderResult } from '@dojo/framework/core/interfaces'; | ||
import { create, tsx } from '@dojo/framework/core/vdom'; | ||
import { radioGroup } from './middleware'; | ||
|
||
type RadioOptions = { value: string; label?: string }[]; | ||
|
||
interface RadioGroupProperties { | ||
/** Initial value of the radio group */ | ||
initialValue?: string; | ||
/** The label to be displayed in the legend */ | ||
label?: string; | ||
/** The name attribute for this form group */ | ||
name: string; | ||
/** Callback for the current value */ | ||
onValue(value: string): void; | ||
/** Object containing the values / labels to create radios for */ | ||
options: RadioOptions; | ||
/** Custom renderer for the radios, receives the radio group middleware and options */ | ||
renderer?( | ||
name: string, | ||
middleware: ReturnType<ReturnType<typeof radioGroup>['api']>, | ||
options: RadioOptions | ||
): RenderResult; | ||
} | ||
|
||
const factory = create({ radioGroup, theme }).properties<RadioGroupProperties>(); | ||
|
||
export const RadioGroup = factory(function({ properties, middleware: { radioGroup, theme } }) { | ||
const { name, label, options, renderer, onValue, initialValue } = properties(); | ||
const radio = radioGroup(onValue, initialValue || ''); | ||
const { root, legend } = theme.classes(css); | ||
|
||
function renderRadios() { | ||
if (renderer) { | ||
return renderer(name, radio, options); | ||
} | ||
return options.map(({ value, label }) => { | ||
const { checked } = radio(value); | ||
return ( | ||
<Radio | ||
checked={checked()} | ||
label={label || value} | ||
name={name} | ||
onValue={checked} | ||
value={value} | ||
/> | ||
); | ||
}); | ||
} | ||
|
||
return ( | ||
<fieldset key="root" classes={root} name={name}> | ||
{label && <legend classes={legend}>{label}</legend>} | ||
{renderRadios()} | ||
</fieldset> | ||
); | ||
}); | ||
|
||
export default RadioGroup; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { create } from '@dojo/framework/core/vdom'; | ||
import { createICacheMiddleware } from '@dojo/framework/core/middleware/icache'; | ||
|
||
interface RadioGroupICache { | ||
initial: string; | ||
value: string; | ||
} | ||
|
||
const icache = createICacheMiddleware<RadioGroupICache>(); | ||
const factory = create({ icache }); | ||
|
||
export const radioGroup = factory(({ middleware: { icache } }) => { | ||
return (onValue: (value: string) => void, initialValue: string) => { | ||
const existingInitialValue = icache.get('initial'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should be typed as a string: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's typed above @JamesLMilner via |
||
|
||
if (existingInitialValue !== initialValue) { | ||
icache.set('value', initialValue); | ||
icache.set('initial', initialValue); | ||
} | ||
|
||
return (key: string) => ({ | ||
checked(checked?: boolean) { | ||
const existingValue = icache.get('value'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should be typed as a string There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as above |
||
|
||
if (!checked && existingValue === key) { | ||
return existingValue === key && true; | ||
} else if (checked && existingValue !== key) { | ||
icache.set('value', key); | ||
onValue(key); | ||
} | ||
} | ||
}); | ||
}; | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
const { describe, it } = intern.getInterface('bdd'); | ||
import * as css from '../../theme/radio-group.m.css'; | ||
import Radio from '../../radio/index'; | ||
import RadioGroup from '../index'; | ||
import assertionTemplate from '@dojo/framework/testing/assertionTemplate'; | ||
import harness from '@dojo/framework/testing/harness'; | ||
import { tsx } from '@dojo/framework/core/vdom'; | ||
|
||
function noop() {} | ||
|
||
describe('RadioGroup', () => { | ||
const template = assertionTemplate(() => ( | ||
<fieldset key="root" classes={css.root} name="test" /> | ||
)); | ||
|
||
it('renders with options', () => { | ||
const h = harness(() => ( | ||
<RadioGroup | ||
name="test" | ||
onValue={noop} | ||
options={[{ value: 'cat' }, { value: 'fish' }, { value: 'dog' }]} | ||
/> | ||
)); | ||
const optionTemplate = template.setChildren('@root', () => [ | ||
<Radio name="test" value="cat" label="cat" checked={undefined} onValue={noop} />, | ||
<Radio name="test" value="fish" label="fish" checked={undefined} onValue={noop} />, | ||
<Radio name="test" value="dog" label="dog" checked={undefined} onValue={noop} /> | ||
]); | ||
h.expect(optionTemplate); | ||
}); | ||
|
||
it('renders with a label', () => { | ||
const h = harness(() => ( | ||
<RadioGroup | ||
label="test label" | ||
name="test" | ||
onValue={noop} | ||
options={[{ value: 'cat' }]} | ||
/> | ||
)); | ||
const labelTemplate = template.setChildren('@root', () => [ | ||
<legend classes={css.legend}>test label</legend>, | ||
<Radio name="test" value="cat" label="cat" checked={undefined} onValue={noop} /> | ||
]); | ||
h.expect(labelTemplate); | ||
}); | ||
|
||
it('renders with initial value', () => { | ||
const h = harness(() => ( | ||
<RadioGroup | ||
initialValue="fish" | ||
name="test" | ||
onValue={noop} | ||
options={[{ value: 'cat' }, { value: 'fish' }, { value: 'dog' }]} | ||
/> | ||
)); | ||
const optionTemplate = template.setChildren('@root', () => [ | ||
<Radio name="test" value="cat" label="cat" checked={undefined} onValue={noop} />, | ||
<Radio name="test" value="fish" label="fish" checked={true} onValue={noop} />, | ||
<Radio name="test" value="dog" label="dog" checked={undefined} onValue={noop} /> | ||
]); | ||
h.expect(optionTemplate); | ||
}); | ||
|
||
it('renders with custom renderer', () => { | ||
const h = harness(() => ( | ||
<RadioGroup | ||
label="custom render label" | ||
name="test" | ||
onValue={noop} | ||
options={[{ value: 'cat' }]} | ||
renderer={() => { | ||
return [ | ||
<span>custom label</span>, | ||
<Radio | ||
name="test" | ||
value="cat" | ||
label="cat" | ||
checked={false} | ||
onValue={noop} | ||
/>, | ||
<hr /> | ||
]; | ||
}} | ||
/> | ||
)); | ||
const customTemplate = template.setChildren('@root', () => [ | ||
<legend classes={css.legend}>custom render label</legend>, | ||
<span>custom label</span>, | ||
<Radio name="test" value="cat" label="cat" checked={false} onValue={noop} />, | ||
<hr /> | ||
]); | ||
h.expect(customTemplate); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you could do
initialValue = ''
here rather than use||
below.